summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Reveman <c99drn@cs.umu.se>2006-02-09 06:03:09 +0000
committerDavid Reveman <c99drn@cs.umu.se>2006-02-09 06:03:09 +0000
commit9959c2b13ded64a5e66359a8097250dc9d87fc1c (patch)
tree23478d196cd4acb4a9d2949c0438e9df75c2e8d6
Initial revision
-rw-r--r--AUTHORS1
-rw-r--r--COPYING3
-rw-r--r--COPYING.GPL340
-rw-r--r--COPYING.MIT21
-rw-r--r--ChangeLog0
-rw-r--r--INSTALL9
-rw-r--r--Makefile.am10
-rw-r--r--NEWS0
-rw-r--r--README8
-rw-r--r--TODO22
-rwxr-xr-xautogen.sh3
-rw-r--r--compiz.pc.in12
-rw-r--r--configure.ac219
-rw-r--r--gnome/Makefile.am22
-rw-r--r--gnome/compiz-window-manager.c400
-rw-r--r--gnome/compiz-window-manager.h39
-rw-r--r--gnome/compiz.desktop13
-rw-r--r--gnome/compiz.desktop.in13
-rw-r--r--gnome/window-decorator/Makefile.am9
-rw-r--r--gnome/window-decorator/TODO6
-rw-r--r--gnome/window-decorator/gnome-window-decorator.c3163
-rw-r--r--images/Makefile.am8
-rw-r--r--images/background.pngbin0 -> 127 bytes
-rw-r--r--images/window.pngbin0 -> 9989 bytes
-rw-r--r--include/Makefile.am2
-rw-r--r--include/compiz.h1605
-rw-r--r--kde/Makefile.am1
-rw-r--r--kde/window-decorator/Makefile.am7
-rw-r--r--plugins/Makefile.am76
-rw-r--r--plugins/cube.c831
-rw-r--r--plugins/decoration.c1321
-rw-r--r--plugins/fade.c720
-rw-r--r--plugins/gconf.c1036
-rw-r--r--plugins/minimize.c820
-rw-r--r--plugins/move.c549
-rw-r--r--plugins/place.c1081
-rw-r--r--plugins/resize.c691
-rw-r--r--plugins/rotate.c1092
-rw-r--r--plugins/scale.c1263
-rw-r--r--plugins/switcher.c1565
-rw-r--r--plugins/wobbly.c2593
-rw-r--r--plugins/zoom.c823
-rw-r--r--src/Makefile.am22
-rw-r--r--src/action.c41
-rw-r--r--src/display.c1844
-rw-r--r--src/event.c1005
-rw-r--r--src/main.c211
-rw-r--r--src/option.c292
-rw-r--r--src/paint.c864
-rw-r--r--src/plugin.c404
-rw-r--r--src/privates.c68
-rw-r--r--src/readpng.c266
-rw-r--r--src/screen.c2521
-rw-r--r--src/texture.c337
-rw-r--r--src/window.c2903
55 files changed, 31175 insertions, 0 deletions
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 00000000..41c6efe7
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1 @@
+David Reveman <davidr@novell.com>
diff --git a/COPYING b/COPYING
new file mode 100644
index 00000000..028ccfe7
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,3 @@
+All code is licensed either under the GPL or the MIT license.
+
+For More information see COPYING.GPL and COPYING.MIT.
diff --git a/COPYING.GPL b/COPYING.GPL
new file mode 100644
index 00000000..d60c31a9
--- /dev/null
+++ b/COPYING.GPL
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/COPYING.MIT b/COPYING.MIT
new file mode 100644
index 00000000..69ef1bfb
--- /dev/null
+++ b/COPYING.MIT
@@ -0,0 +1,21 @@
+
+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. \ No newline at end of file
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ChangeLog
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 00000000..038f6610
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,9 @@
+compiz uses automake, in order to generate the Makefiles for compiz use:
+
+ $ autogen.sh
+
+After that, standard build procedures apply:
+
+ $ make
+ # make install
+
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 00000000..3c7c0b13
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,10 @@
+SUBDIRS = include src plugins images gnome kde
+
+EXTRA_DIST = \
+ COPYING \
+ COPYING.GPL \
+ COPYING.MIT \
+ compiz.pc.in
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = compiz.pc
diff --git a/NEWS b/NEWS
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/NEWS
diff --git a/README b/README
new file mode 100644
index 00000000..d57217ee
--- /dev/null
+++ b/README
@@ -0,0 +1,8 @@
+compiz - OpenGL window and compositing manager
+
+Compiz is an OpenGL compositing manager that use GLX_EXT_texture_from_pixmap
+for binding redirected top-level windows to texture objects. It has a flexible
+plug-in system and it is designed to run well on most graphics hardware.
+
+David Reveman
+davidr@novell.com
diff --git a/TODO b/TODO
new file mode 100644
index 00000000..ac6bbc94
--- /dev/null
+++ b/TODO
@@ -0,0 +1,22 @@
+
+General:
+
+* Session management
+
+* Various window management improvements
+
+* Window shade mode
+
+* Support for multiple desktops
+
+* Multi-screen support
+
+
+KDE:
+
+* Remove glib dependency from place plugin
+
+* QT/KDE configuration plugin similar to the gconf plugin
+
+* QT/KDE decoration program
+
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 00000000..fa9b3b9c
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+autoreconf -v --install || exit 1
+./configure "$@"
diff --git a/compiz.pc.in b/compiz.pc.in
new file mode 100644
index 00000000..bd6ba1c5
--- /dev/null
+++ b/compiz.pc.in
@@ -0,0 +1,12 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: glxcompmgr
+Description: OpenGL compositing manager
+Version: @VERSION@
+
+Requires: @GLXCOMP_REQUIRES@
+Libs: @GLXCOMP_LIBS@ @GL_LIBS@
+Cflags: @GLXCOMP_CFLAGS@ @GL_CFLAGS@ -I${includedir}/glxcomp
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 00000000..ce985e82
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,219 @@
+AC_PREREQ(2.57)
+
+AC_INIT([compiz], [0.0.1], [davidr@novell.com])
+
+AC_CONFIG_AUX_DIR(config)
+
+AM_INIT_AUTOMAKE([dist-bzip2])
+AC_CONFIG_HEADER([config.h])
+AM_MAINTAINER_MODE
+
+AC_ISC_POSIX
+AC_PROG_CC
+AC_PROG_CPP
+AC_PROG_LIBTOOL
+AC_HEADER_STDC
+AC_CHECK_HEADERS([stdlib.h sys/time.h unistd.h])
+
+if test "x$GCC" = "xyes"; then
+ case " $CFLAGS " in
+ *[[\ \ ]]-Wall[[\ \ ]]*) ;;
+ *) CFLAGS="$CFLAGS -Wall" ;;
+ esac
+
+ case " $CFLAGS " in
+ *[[\ \ ]]-Wpointer-arith[[\ \ ]]*) ;;
+ *) CFLAGS="$CFLAGS -Wpointer-arith" ;;
+ esac
+
+ case " $CFLAGS " in
+ *[[\ \ ]]-Wstrict-prototypes[[\ \ ]]*) ;;
+ *) CFLAGS="$CFLAGS -Wstrict-prototypes" ;;
+ esac
+
+ case " $CFLAGS " in
+ *[[\ \ ]]-Wmissing-prototypes[[\ \ ]]*) ;;
+ *) CFLAGS="$CFLAGS -Wmissing-prototypes" ;;
+ esac
+
+ case " $CFLAGS " in
+ *[[\ \ ]]-Wmissing-declarations[[\ \ ]]*) ;;
+ *) CFLAGS="$CFLAGS -Wmissing-declarations" ;;
+ esac
+
+ case " $CFLAGS " in
+ *[[\ \ ]]-Wnested-externs[[\ \ ]]*) ;;
+ *) CFLAGS="$CFLAGS -Wnested-externs" ;;
+ esac
+
+ case " $CFLAGS " in
+ *[[\ \ ]]-fno-strict-aliasing[[\ \ ]]*) ;;
+ *) CFLAGS="$CFLAGS -fno-strict-aliasing" ;;
+ esac
+
+ if test "x$enable_ansi" = "xyes"; then
+ case " $CFLAGS " in
+ *[[\ \ ]]-ansi[[\ \ ]]*) ;;
+ *) CFLAGS="$CFLAGS -ansi" ;;
+ esac
+
+ case " $CFLAGS " in
+ *[[\ \ ]]-pedantic[[\ \ ]]*) ;;
+ *) CFLAGS="$CFLAGS -pedantic" ;;
+ esac
+ fi
+fi
+
+AC_C_BIGENDIAN
+
+plugindir=$libdir/compiz
+AC_SUBST(plugindir)
+
+imagedir=$datadir/compiz
+AC_SUBST(imagedir)
+
+COMPIZ_REQUIRES="libpng \
+ xcomposite \
+ xfixes \
+ xdamage \
+ xrandr \
+ ice \
+ sm \
+ libstartup-notification-1.0 >= 0.7"
+
+PKG_CHECK_MODULES(COMPIZ, $COMPIZ_REQUIRES)
+AC_SUBST(COMPIZ_REQUIRES)
+
+AC_MSG_CHECKING(for GL_CFLAGS)
+AC_ARG_WITH(gl-cflags, [ --with-gl-cflags=CFLAGS ],
+ [GL_CFLAGS="$withval"],
+ [GL_CFLAGS=""])
+
+AC_MSG_RESULT($GL_CFLAGS)
+AC_MSG_CHECKING(for GL_LIBS)
+AC_ARG_WITH(gl-libs, [ --with-gl-libs=LIBS ],
+ [GL_LIBS="$withval"],
+ [GL_LIBS="-lGL"])
+AC_MSG_RESULT($GL_LIBS)
+
+AC_SUBST(GL_CFLAGS)
+AC_SUBST(GL_LIBS)
+
+AC_ARG_ENABLE(gconf,
+ [ --disable-gconf Disable gconf plugin],
+ [use_gconf=$enableval], [use_gconf=yes])
+
+if test "x$use_gconf" = "xyes"; then
+ PKG_CHECK_MODULES(GCONF, gconf-2.0, [use_gconf=yes], [use_gconf=no])
+fi
+
+AM_CONDITIONAL(GCONF_PLUGIN, test "x$use_gconf" = "xyes")
+if test "$use_gconf" = yes; then
+ AC_DEFINE(USE_GCONF, 1, [Build gconf plugin])
+fi
+
+AC_ARG_ENABLE(place,
+ [ --disable-place Disable window placement plugin],
+ [use_place=$enableval], [use_place=yes])
+
+if test "x$use_place" = "xyes"; then
+ PKG_CHECK_MODULES(PLACE, glib-2.0, [use_place=yes], [use_place=no])
+fi
+
+AM_CONDITIONAL(PLACE_PLUGIN, test "x$use_place" = "xyes")
+if test "$use_place" = yes; then
+ AC_DEFINE(USE_PLACE, 1, [Build placement plugin])
+fi
+
+AC_ARG_ENABLE(menu,
+ [ --disable-menu Disable window menu plugin],
+ [use_menu=$enableval], [use_menu=yes])
+
+if test "x$use_menu" = "xyes"; then
+ PKG_CHECK_MODULES(MENU, gtk+-2.0, [use_menu=yes], [use_menu=no])
+fi
+
+AM_CONDITIONAL(MENU_PLUGIN, test "x$use_menu" = "xyes")
+if test "$use_menu" = yes; then
+ AC_DEFINE(USE_MENU, 1, [Build menu plugin])
+fi
+
+AC_ARG_ENABLE(libsvg-cairo,
+ [ --enable-libsvg-cairo Enable svg support],
+ [use_libsvg_cairo=$enableval], [use_libsvg_cairo=no])
+
+if test "x$use_libsvg_cairo" = "xyes"; then
+ PKG_CHECK_MODULES(LIBSVG_CAIRO, libsvg-cairo,
+ [use_libsvg_cairo=yes],
+ [use_libsvg_cairo=no])
+fi
+
+AM_CONDITIONAL(USE_LIBSVG_CAIRO, [test x$use_libsvg_cairo = xyes])
+if test "$use_libsvg_cairo" = yes; then
+ AC_DEFINE(USE_LIBSVG_CAIRO, 1, [libsvg-cairo for SVG support])
+fi
+
+AC_ARG_ENABLE(gnome,
+ [ --disable-gnome Disable gnome window decorator and settings module],
+ [use_gnome=$enableval], [use_gnome=yes])
+
+if test "x$use_gnome" = "xyes"; then
+ PKG_CHECK_MODULES(GNOME_WINDOW_DECORATOR,
+ gtk+-2.0 libwnck-1.0 pangocairo,
+ [use_gnome=yes], [use_gnome=no])
+ if test "x$use_gnome" = "xyes"; then
+ PKG_CHECK_MODULES(GNOME_WINDOW_SETTINGS,
+ gnome-window-settings-2.0 gnome-desktop-2.0,
+ [use_gnome=yes], [use_gnome=no])
+ fi
+ windowsettingsdatadir=`pkg-config --variable=prefix gnome-window-settings-2.0`/share
+ windowsettingslibdir=`pkg-config --variable=libdir gnome-window-settings-2.0`
+fi
+
+AC_SUBST(windowsettingsdatadir)
+AC_SUBST(windowsettingslibdir)
+
+AM_CONDITIONAL(USE_GNOME, test "x$use_gnome" = "xyes")
+if test "$use_gnome" = yes; then
+ AC_DEFINE(USE_GNOME, 1, [Build gnome window decorator and settings module])
+fi
+
+AC_ARG_ENABLE(kde,
+ [ --disable-kde Disable kde window decorator],
+ [use_kde=$enableval], [use_kde=yes])
+
+if test "x$use_kde" = "xyes"; then
+ PKG_CHECK_MODULES(KDE_WINDOW_DECORATOR, QtCore,
+ [use_kde=yes], [use_kde=no])
+fi
+
+AM_CONDITIONAL(USE_KDE, test "x$use_kde" = "xyes")
+if test "$use_kde" = yes; then
+ AC_DEFINE(USE_KDE, 1, [Build kde window decorator])
+fi
+
+AC_OUTPUT([
+compiz.pc
+Makefile
+src/Makefile
+include/Makefile
+plugins/Makefile
+images/Makefile
+gnome/Makefile
+gnome/window-decorator/Makefile
+gnome/compiz.desktop
+kde/Makefile
+kde/window-decorator/Makefile
+])
+
+echo ""
+echo "the following optional plugins will be compiled:"
+echo " gconf: $use_gconf"
+echo " place: $use_place"
+echo " menu: $use_menu"
+echo " svg: $use_libsvg_cairo"
+echo ""
+echo "and the following optional features will be compiled:"
+echo " gnome: $use_gnome"
+echo " kde: $use_kde"
+echo ""
diff --git a/gnome/Makefile.am b/gnome/Makefile.am
new file mode 100644
index 00000000..29a56247
--- /dev/null
+++ b/gnome/Makefile.am
@@ -0,0 +1,22 @@
+SUBDIRS = window-decorator
+
+if USE_GNOME
+libcompiz_la_LDFLAGS = -export-dynamic -avoid-version
+libcompiz_la_LIBADD = @GNOME_WINDOW_SETTINGS_LIBS@
+libcompiz_la_SOURCES = \
+ compiz-window-manager.c \
+ compiz-window-manager.h
+libcompiz_module = libcompiz.la
+
+desktopfilesdir = $(windowsettingsdatadir)/gnome/wm-properties
+desktopfiles_in_files = compiz.desktop.in
+desktopfiles_files = $(desktopfiles_in_files:.desktop.in=.desktop)
+desktopfiles_DATA = $(desktopfiles_files)
+endif
+
+INCLUDES = @GNOME_WINDOW_SETTINGS_CFLAGS@
+
+moduledir = $(windowsettingslibdir)/window-manager-settings
+
+module_LTLIBRARIES = \
+ $(libcompiz_module)
diff --git a/gnome/compiz-window-manager.c b/gnome/compiz-window-manager.c
new file mode 100644
index 00000000..75e5779f
--- /dev/null
+++ b/gnome/compiz-window-manager.c
@@ -0,0 +1,400 @@
+/*
+ * 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 <config.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <string.h>
+#include <gconf/gconf-client.h>
+
+#include "compiz-window-manager.h"
+
+#define COMPIZ_CLICK_TO_FOCUS_KEY \
+ "/apps/compiz/general/allscreens/options/click_to_focus"
+
+#define COMPIZ_AUTORAISE_KEY \
+ "/apps/compiz/general/allscreens/options/autoraise"
+
+#define COMPIZ_AUTORAISE_DELAY_KEY \
+ "/apps/compiz/general/allscreens/options/autoraise_delay"
+
+#define COMPIZ_MOUSE_MOVE_KEY \
+ "/apps/compiz/plugins/move/screen0/options/initiate"
+
+#define COMPIZ_DOUBLE_CLICK_TITLEBAR_KEY \
+ "/apps/compiz/general/allscreens/options/action_double_click_titlebar"
+
+enum {
+ DOUBLE_CLICK_MAXIMIZE
+};
+
+static GnomeWindowManagerClass *parent_class;
+
+struct _CompizWindowManagerPrivate {
+ GConfClient *gconf;
+ char *mouse_modifier;
+};
+
+static void
+value_changed (GConfClient *client,
+ const gchar *key,
+ GConfValue *value,
+ void *data)
+{
+ CompizWindowManager *wm;
+
+ wm = COMPIZ_WINDOW_MANAGER (data);
+
+ gnome_window_manager_settings_changed (GNOME_WINDOW_MANAGER (wm));
+}
+
+/* this function is called when the shared lib is loaded */
+GObject *
+window_manager_new (int expected_interface_version)
+{
+ GObject *wm;
+
+ if (expected_interface_version != GNOME_WINDOW_MANAGER_INTERFACE_VERSION)
+ {
+ g_warning ("Compiz window manager module wasn't compiled with the "
+ "current version of gnome-control-center");
+ return NULL;
+ }
+
+ wm = g_object_new (compiz_window_manager_get_type (), NULL);
+
+ return wm;
+}
+
+static void
+compiz_change_settings (GnomeWindowManager *wm,
+ const GnomeWMSettings *settings)
+{
+ CompizWindowManager *cwm;
+
+ cwm = COMPIZ_WINDOW_MANAGER (wm);
+
+ if (settings->flags & GNOME_WM_SETTING_MOUSE_FOCUS)
+ gconf_client_set_bool (cwm->p->gconf,
+ COMPIZ_CLICK_TO_FOCUS_KEY,
+ settings->focus_follows_mouse == FALSE,
+ NULL);
+
+ if (settings->flags & GNOME_WM_SETTING_AUTORAISE)
+ gconf_client_set_bool (cwm->p->gconf,
+ COMPIZ_AUTORAISE_KEY,
+ settings->autoraise, NULL);
+
+ if (settings->flags & GNOME_WM_SETTING_AUTORAISE_DELAY)
+ gconf_client_set_int (cwm->p->gconf,
+ COMPIZ_AUTORAISE_DELAY_KEY,
+ settings->autoraise_delay, NULL);
+
+ if (settings->flags & GNOME_WM_SETTING_MOUSE_MOVE_MODIFIER)
+ {
+ char *value;
+
+ value = g_strdup_printf ("<%s>Button1", settings->mouse_move_modifier);
+ gconf_client_set_string (cwm->p->gconf,
+ COMPIZ_MOUSE_MOVE_KEY,
+ value, NULL);
+ g_free (value);
+ }
+
+/*
+ if (settings->flags & GNOME_WM_SETTING_DOUBLE_CLICK_ACTION)
+ {
+ const char *action = NULL;
+
+ switch (settings->double_click_action) {
+ case DOUBLE_CLICK_SHADE:
+ action = "toggle_shade";
+ break;
+ case DOUBLE_CLICK_MAXIMIZE:
+ action = "toggle_maximize";
+ break;
+ }
+
+ if (action)
+ gconf_client_set_string (meta_wm->p->gconf,
+ COMPIZ_DOUBLE_CLICK_TITLEBAR_KEY,
+ action, NULL);
+ }
+*/
+
+}
+
+static void
+compiz_get_settings (GnomeWindowManager *wm,
+ GnomeWMSettings *settings)
+{
+ CompizWindowManager *cwm;
+ int to_get;
+
+ cwm = COMPIZ_WINDOW_MANAGER (wm);
+
+ to_get = settings->flags;
+ settings->flags = 0;
+
+ if (to_get & GNOME_WM_SETTING_MOUSE_FOCUS)
+ {
+ settings->focus_follows_mouse =
+ gconf_client_get_bool (cwm->p->gconf,
+ COMPIZ_CLICK_TO_FOCUS_KEY, NULL) == FALSE;
+
+ settings->flags |= GNOME_WM_SETTING_MOUSE_FOCUS;
+ }
+
+ if (to_get & GNOME_WM_SETTING_AUTORAISE)
+ {
+ settings->autoraise = FALSE;
+
+ settings->autoraise = gconf_client_get_bool (cwm->p->gconf,
+ COMPIZ_AUTORAISE_KEY,
+ NULL);
+
+ settings->flags |= GNOME_WM_SETTING_AUTORAISE;
+ }
+
+ if (to_get & GNOME_WM_SETTING_AUTORAISE_DELAY)
+ {
+ settings->autoraise_delay =
+ gconf_client_get_int (cwm->p->gconf,
+ COMPIZ_AUTORAISE_DELAY_KEY,
+ NULL);
+
+ settings->flags |= GNOME_WM_SETTING_AUTORAISE_DELAY;
+ }
+
+ if (to_get & GNOME_WM_SETTING_MOUSE_MOVE_MODIFIER)
+ {
+ const char *new;
+ char *str;
+
+ str = gconf_client_get_string (cwm->p->gconf,
+ COMPIZ_MOUSE_MOVE_KEY,
+ NULL);
+
+ if (str == NULL)
+ str = g_strdup ("<Super>");
+
+ if (strncmp (str, "<Super>", 7) == 0)
+ new = "Super";
+ else if (strncmp (str, "<Alt>", 5) == 0)
+ new = "Alt";
+ else if (strncmp (str, "<Meta>", 6) == 0)
+ new = "Meta";
+ else if (strncmp (str, "<Hyper>", 7) == 0)
+ new = "Hyper";
+ else if (strncmp (str, "<Control>", 9) == 0)
+ new = "Control";
+ else
+ new = NULL;
+
+ if (new && cwm->p->mouse_modifier &&
+ strcmp (new, cwm->p->mouse_modifier) == 0)
+ {
+ /* unchanged */;
+ }
+ else
+ {
+ g_free (cwm->p->mouse_modifier);
+ cwm->p->mouse_modifier = g_strdup (new ? new : "");
+ }
+
+ g_free (str);
+
+ settings->mouse_move_modifier = cwm->p->mouse_modifier;
+
+ settings->flags |= GNOME_WM_SETTING_MOUSE_MOVE_MODIFIER;
+ }
+
+ if (to_get & GNOME_WM_SETTING_DOUBLE_CLICK_ACTION)
+ {
+/*
+ char *str;
+
+ str = gconf_client_get_string (cwm->p->gconf,
+ COMPIZ_DOUBLE_CLICK_TITLEBAR_KEY,
+ NULL);
+
+ if (str == NULL)
+ str = g_strdup ("toggle_shade");
+
+ if (strcmp (str, "toggle_shade") == 0)
+ settings->double_click_action = DOUBLE_CLICK_SHADE;
+ else if (strcmp (str, "toggle_maximize") == 0)
+ settings->double_click_action = DOUBLE_CLICK_MAXIMIZE;
+ else
+ settings->double_click_action = DOUBLE_CLICK_SHADE;
+
+ g_free (str);
+
+ settings->flags |= GNOME_WM_SETTING_DOUBLE_CLICK_ACTION;
+*/
+ }
+}
+
+static int
+compiz_get_settings_mask (GnomeWindowManager *wm)
+{
+ return
+/* GNOME_WM_SETTING_FONT | */
+ GNOME_WM_SETTING_MOUSE_FOCUS |
+ GNOME_WM_SETTING_AUTORAISE |
+ GNOME_WM_SETTING_AUTORAISE_DELAY |
+ GNOME_WM_SETTING_MOUSE_MOVE_MODIFIER |
+/* GNOME_WM_SETTING_THEME | */
+/* GNOME_WM_SETTING_DOUBLE_CLICK_ACTION */ 0;
+}
+
+static GList *
+compiz_get_theme_list (GnomeWindowManager *wm)
+{
+ return NULL;
+}
+
+static char *
+compiz_get_user_theme_folder (GnomeWindowManager *wm)
+{
+ return NULL;
+}
+
+static void
+compiz_get_double_click_actions (GnomeWindowManager *wm,
+ const GnomeWMDoubleClickAction **actions_p,
+ int *n_actions_p)
+{
+ static GnomeWMDoubleClickAction actions[] = {
+ { DOUBLE_CLICK_MAXIMIZE, "Maximize" }
+ };
+ static gboolean initialized = FALSE;
+
+ if (!initialized)
+ {
+ int i = 0;
+
+ initialized = TRUE;
+ while (i < (int) G_N_ELEMENTS (actions))
+ {
+ g_assert (actions[i].number == i);
+ actions[i].human_readable_name = actions[i].human_readable_name;
+
+ ++i;
+ }
+ }
+
+ *actions_p = actions;
+ *n_actions_p = (int) G_N_ELEMENTS (actions);
+}
+
+static void
+compiz_window_manager_init (CompizWindowManager *cwm,
+ CompizWindowManagerClass *class)
+{
+ cwm->p = g_new0 (CompizWindowManagerPrivate, 1);
+ cwm->p->gconf = gconf_client_get_default ();
+ cwm->p->mouse_modifier = NULL;
+
+ gconf_client_add_dir (cwm->p->gconf,
+ "/apps/compiz",
+ GCONF_CLIENT_PRELOAD_ONELEVEL,
+ NULL);
+
+ g_signal_connect (G_OBJECT (cwm->p->gconf),
+ "value_changed",
+ G_CALLBACK (value_changed),
+ cwm);
+}
+
+static void
+compiz_window_manager_finalize (GObject *object)
+{
+ CompizWindowManager *cwm;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (IS_COMPIZ_WINDOW_MANAGER (object));
+
+ cwm = COMPIZ_WINDOW_MANAGER (object);
+
+ g_signal_handlers_disconnect_by_func (G_OBJECT (cwm->p->gconf),
+ G_CALLBACK (value_changed),
+ cwm);
+
+ g_object_unref (G_OBJECT (cwm->p->gconf));
+ g_free (cwm->p);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+compiz_window_manager_class_init (CompizWindowManagerClass *class)
+{
+ GObjectClass *object_class;
+ GnomeWindowManagerClass *wm_class;
+
+ object_class = G_OBJECT_CLASS (class);
+ wm_class = GNOME_WINDOW_MANAGER_CLASS (class);
+
+ object_class->finalize = compiz_window_manager_finalize;
+
+ wm_class->change_settings = compiz_change_settings;
+ wm_class->get_settings = compiz_get_settings;
+ wm_class->get_settings_mask = compiz_get_settings_mask;
+ wm_class->get_theme_list = compiz_get_theme_list;
+ wm_class->get_user_theme_folder = compiz_get_user_theme_folder;
+ wm_class->get_double_click_actions = compiz_get_double_click_actions;
+
+ parent_class = g_type_class_peek_parent (class);
+}
+
+GType
+compiz_window_manager_get_type (void)
+{
+ static GType compiz_window_manager_type = 0;
+
+ if (!compiz_window_manager_type)
+ {
+ static GTypeInfo compiz_window_manager_info = {
+ sizeof (CompizWindowManagerClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) compiz_window_manager_class_init,
+ NULL,
+ NULL,
+ sizeof (CompizWindowManager),
+ 0,
+ (GInstanceInitFunc) compiz_window_manager_init,
+ NULL
+ };
+
+ compiz_window_manager_type =
+ g_type_register_static (gnome_window_manager_get_type (),
+ "CompizWindowManager",
+ &compiz_window_manager_info, 0);
+ }
+
+ return compiz_window_manager_type;
+}
diff --git a/gnome/compiz-window-manager.h b/gnome/compiz-window-manager.h
new file mode 100644
index 00000000..5506e63b
--- /dev/null
+++ b/gnome/compiz-window-manager.h
@@ -0,0 +1,39 @@
+#ifndef COMPIZ_WINDOW_MANAGER_H
+#define COMPIZ_WINDOW_MANAGER_H
+
+#include <glib-object.h>
+
+#include "gnome-window-manager.h"
+
+#define COMPIZ_WINDOW_MANAGER(obj) \
+ G_TYPE_CHECK_INSTANCE_CAST (obj, compiz_window_manager_get_type (), \
+ CompizWindowManager)
+
+#define COMPIZ_WINDOW_MANAGER_CLASS(klass) \
+ G_TYPE_CHECK_CLASS_CAST (klass, compiz_window_manager_get_type (), \
+ MetacityWindowManagerClass)
+
+#define IS_COMPIZ_WINDOW_MANAGER(obj) \
+ G_TYPE_CHECK_INSTANCE_TYPE (obj, compiz_window_manager_get_type ())
+
+
+typedef struct _CompizWindowManager CompizWindowManager;
+typedef struct _CompizWindowManagerClass CompizWindowManagerClass;
+typedef struct _CompizWindowManagerPrivate CompizWindowManagerPrivate;
+
+struct _CompizWindowManager {
+ GnomeWindowManager parent;
+ CompizWindowManagerPrivate *p;
+};
+
+struct _CompizWindowManagerClass {
+ GnomeWindowManagerClass klass;
+};
+
+GType
+compiz_window_manager_get_type (void);
+
+GObject *
+window_manager_new (int expected_interface_version);
+
+#endif
diff --git a/gnome/compiz.desktop b/gnome/compiz.desktop
new file mode 100644
index 00000000..cde9bae4
--- /dev/null
+++ b/gnome/compiz.desktop
@@ -0,0 +1,13 @@
+[Desktop Entry]
+Encoding=UTF-8
+_Name=Compiz
+Exec=compiz
+# name of loadable control center module
+X-GNOME-WMSettingsModule=compiz
+# name we put on the WM spec check window
+X-GNOME-WMName=compiz
+# back compat only
+X-GnomeWMSettingsLibrary=compiz
+
+[Window Manager]
+SessionManaged=false
diff --git a/gnome/compiz.desktop.in b/gnome/compiz.desktop.in
new file mode 100644
index 00000000..cde9bae4
--- /dev/null
+++ b/gnome/compiz.desktop.in
@@ -0,0 +1,13 @@
+[Desktop Entry]
+Encoding=UTF-8
+_Name=Compiz
+Exec=compiz
+# name of loadable control center module
+X-GNOME-WMSettingsModule=compiz
+# name we put on the WM spec check window
+X-GNOME-WMName=compiz
+# back compat only
+X-GnomeWMSettingsLibrary=compiz
+
+[Window Manager]
+SessionManaged=false
diff --git a/gnome/window-decorator/Makefile.am b/gnome/window-decorator/Makefile.am
new file mode 100644
index 00000000..9702084a
--- /dev/null
+++ b/gnome/window-decorator/Makefile.am
@@ -0,0 +1,9 @@
+if USE_GNOME
+gnome_window_decorator_LDADD = @GNOME_WINDOW_DECORATOR_LIBS@
+gnome_window_decorator_SOURCES = gnome-window-decorator.c
+gnome_window_decorator_program = gnome-window-decorator
+endif
+
+INCLUDES = @GNOME_WINDOW_DECORATOR_CFLAGS@
+
+bin_PROGRAMS = $(gnome_window_decorator_program) \ No newline at end of file
diff --git a/gnome/window-decorator/TODO b/gnome/window-decorator/TODO
new file mode 100644
index 00000000..d1b8e931
--- /dev/null
+++ b/gnome/window-decorator/TODO
@@ -0,0 +1,6 @@
+
+* Plugin interface
+
+* Plugin with SVG-based theme support
+
+* Plugin that supports old metacity themes \ No newline at end of file
diff --git a/gnome/window-decorator/gnome-window-decorator.c b/gnome/window-decorator/gnome-window-decorator.c
new file mode 100644
index 00000000..871c75a1
--- /dev/null
+++ b/gnome/window-decorator/gnome-window-decorator.c
@@ -0,0 +1,3163 @@
+/*
+ * Copyright © 2006 Novell, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/cursorfont.h>
+
+#ifndef GTK_DISABLE_DEPRECATED
+#define GTK_DISABLE_DEPRECATED
+#endif
+
+#include <gtk/gtk.h>
+#include <gtk/gtkwindow.h>
+#include <gdk/gdkx.h>
+
+#define WNCK_I_KNOW_THIS_IS_UNSTABLE
+#include <libwnck/libwnck.h>
+#include <libwnck/window-action-menu.h>
+
+#include <cairo.h>
+#include <cairo-xlib.h>
+
+#if CAIRO_VERSION < CAIRO_VERSION_ENCODE(1, 1, 0)
+#define CAIRO_EXTEND_PAD CAIRO_EXTEND_NONE
+#endif
+
+#include <pango/pango-context.h>
+#include <pango/pangocairo.h>
+
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+#include <limits.h>
+
+#define LEFT_SPACE 12
+#define RIGHT_SPACE 14
+#define TOP_SPACE 27
+#define BOTTOM_SPACE 14
+
+#define ICON_SPACE 20
+#define BUTTON_SPACE 52
+
+typedef struct _extents {
+ gint left;
+ gint right;
+ gint top;
+ gint bottom;
+} extents;
+
+#define GRAVITY_WEST (0)
+#define GRAVITY_EAST (1 << 0)
+#define GRAVITY_NORTH (0)
+#define GRAVITY_SOUTH (1 << 1)
+
+#define ALIGN_LEFT (0)
+#define ALIGN_RIGHT (1 << 0)
+#define ALIGN_TOP (0)
+#define ALIGN_BOTTOM (1 << 1)
+
+#define XX_MASK (1 << 6)
+#define XY_MASK (1 << 7)
+#define YX_MASK (1 << 8)
+#define YY_MASK (1 << 9)
+
+#define WM_MOVERESIZE_SIZE_TOPLEFT 0
+#define WM_MOVERESIZE_SIZE_TOP 1
+#define WM_MOVERESIZE_SIZE_TOPRIGHT 2
+#define WM_MOVERESIZE_SIZE_RIGHT 3
+#define WM_MOVERESIZE_SIZE_BOTTOMRIGHT 4
+#define WM_MOVERESIZE_SIZE_BOTTOM 5
+#define WM_MOVERESIZE_SIZE_BOTTOMLEFT 6
+#define WM_MOVERESIZE_SIZE_LEFT 7
+#define WM_MOVERESIZE_MOVE 8
+#define WM_MOVERESIZE_SIZE_KEYBOARD 9
+#define WM_MOVERESIZE_MOVE_KEYBOARD 10
+
+typedef struct _point {
+ gint x;
+ gint y;
+ gint gravity;
+} point;
+
+typedef struct _quad {
+ point p1;
+ point p2;
+ gint max_width;
+ gint max_height;
+ gint align;
+ cairo_matrix_t m;
+} quad;
+
+#ifdef __SUNPRO_C
+#pragma align 4 (_shadow)
+#endif
+#ifdef __GNUC__
+static const guint8 _shadow[] __attribute__ ((__aligned__ (4))) =
+#else
+ static const guint8 _shadow[] =
+#endif
+{ ""
+ /* Pixbuf magic (0x47646b50) */
+ "GdkP"
+ /* length: header (24) + pixel_data (400) */
+ "\0\0\1\250"
+ /* pixdata_type (0x1010002) */
+ "\1\1\0\2"
+ /* rowstride (40) */
+ "\0\0\0("
+ /* width (10) */
+ "\0\0\0\12"
+ /* height (10) */
+ "\0\0\0\12"
+ /* pixel_data: */
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\2\0\0\0\2\0\0\0\1\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\3\0\0\0\5\0\0\0\7\0\0\0\7\0\0\0\5"
+ "\0\0\0\3\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\3\0\0\0\7\0\0\0\15\0\0\0\22\0"
+ "\0\0\22\0\0\0\15\0\0\0\7\0\0\0\3\0\0\0\1\0\0\0\1\0\0\0\5\0\0\0\15\0\0"
+ "\0\30\0\0\0\40\0\0\0\40\0\0\0\30\0\0\0\15\0\0\0\5\0\0\0\1\0\0\0\2\0\0"
+ "\0\7\0\0\0\22\0\0\0\40\377\377\377\377\0\0\0+\0\0\0\40\0\0\0\22\0\0\0"
+ "\7\0\0\0\2\0\0\0\2\0\0\0\7\0\0\0\22\0\0\0\40\0\0\0+\0\0\0+\0\0\0\40\0"
+ "\0\0\22\0\0\0\7\0\0\0\2\0\0\0\1\0\0\0\5\0\0\0\15\0\0\0\30\0\0\0\40\0"
+ "\0\0\40\0\0\0\30\0\0\0\15\0\0\0\5\0\0\0\1\0\0\0\1\0\0\0\3\0\0\0\7\0\0"
+ "\0\15\0\0\0\22\0\0\0\22\0\0\0\15\0\0\0\7\0\0\0\3\0\0\0\1\0\0\0\0\0\0"
+ "\0\1\0\0\0\3\0\0\0\5\0\0\0\7\0\0\0\7\0\0\0\5\0\0\0\3\0\0\0\1\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\2\0\0\0\2\0\0\0\1\0\0\0\0\0\0"
+ "\0\0\0\0\0\0"
+};
+
+static extents _shadow_extents = { 0, 0, 0, 0 };
+
+static quad _shadow_quads[] = {
+ {
+ { -4, -4, GRAVITY_NORTH | GRAVITY_WEST },
+ { 0, 0, GRAVITY_NORTH | GRAVITY_WEST },
+ SHRT_MAX, SHRT_MAX,
+ 0,
+ {
+ 1.0, 0.0,
+ 0.0, 1.0,
+ 0.0, 0.0
+ }
+ }, {
+ { 0, -4, GRAVITY_NORTH | GRAVITY_WEST },
+ { 0, 0, GRAVITY_NORTH | GRAVITY_EAST },
+ SHRT_MAX, SHRT_MAX,
+ 0,
+ {
+ 0.0, 0.0,
+ 0.0, 1.0,
+ 4.0, 0.0
+ }
+ }, {
+ { 0, -4, GRAVITY_NORTH | GRAVITY_EAST },
+ { 5, 0, GRAVITY_NORTH | GRAVITY_EAST },
+ SHRT_MAX, SHRT_MAX,
+ 0,
+ {
+ 1.0, 0.0,
+ 0.0, 1.0,
+ 4.0, 0.0
+ }
+ },
+
+ {
+ { -4, 0, GRAVITY_NORTH | GRAVITY_WEST },
+ { 0, 0, GRAVITY_SOUTH | GRAVITY_WEST },
+ SHRT_MAX, SHRT_MAX,
+ 0,
+ {
+ 1.0, 0.0,
+ 0.0, 0.0,
+ 0.0, 4.0
+ }
+ }, {
+
+ { 0, 0, GRAVITY_NORTH | GRAVITY_EAST },
+ { 5, 0, GRAVITY_SOUTH | GRAVITY_EAST },
+ SHRT_MAX, SHRT_MAX,
+ 0,
+ {
+ 1.0, 0.0,
+ 0.0, 0.0,
+ 5.0, 4.0
+ }
+ },
+
+ {
+ { -4, 0, GRAVITY_SOUTH | GRAVITY_WEST },
+ { 0, 5, GRAVITY_SOUTH | GRAVITY_WEST },
+ SHRT_MAX, SHRT_MAX,
+ 0,
+ {
+ 1.0, 0.0,
+ 0.0, 1.0,
+ 0.0, 5.0
+ }
+ }, {
+ { 0, 0, GRAVITY_SOUTH | GRAVITY_WEST },
+ { 0, 5, GRAVITY_SOUTH | GRAVITY_EAST },
+ SHRT_MAX, SHRT_MAX,
+ 0,
+ {
+ 0.0, 0.0,
+ 0.0, 1.0,
+ 4.0, 5.0
+ }
+ }, {
+ { 0, 0, GRAVITY_SOUTH | GRAVITY_EAST },
+ { 5, 5, GRAVITY_SOUTH | GRAVITY_EAST },
+ SHRT_MAX, SHRT_MAX,
+ 0,
+ {
+ 1.0, 0.0,
+ 0.0, 1.0,
+ 5.0, 5.0
+ }
+ }
+};
+
+#ifdef __SUNPRO_C
+#pragma align 4 (_large_shadow)
+#endif
+#ifdef __GNUC__
+static const guint8 _large_shadow[] __attribute__ ((__aligned__ (4))) =
+#else
+ static const guint8 _large_shadow[] =
+#endif
+{ ""
+ /* Pixbuf magic (0x47646b50) */
+ "GdkP"
+ /* length: header (24) + pixel_data (2916) */
+ "\0\0\13|"
+ /* pixdata_type (0x1010002) */
+ "\1\1\0\2"
+ /* rowstride (108) */
+ "\0\0\0l"
+ /* width (27) */
+ "\0\0\0\33"
+ /* height (27) */
+ "\0\0\0\33"
+ /* pixel_data: */
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\2\0\0\0\2\0\0\0\2\0\0\0\2\0\0\0\2\0\0"
+ "\0\2\0\0\0\2\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\1\0\0\0\1\0\0\0\2\0\0\0\3\0\0\0\4\0\0\0\5\0\0\0\5\0\0\0\6\0\0\0\6"
+ "\0\0\0\6\0\0\0\5\0\0\0\4\0\0\0\4\0\0\0\2\0\0\0\2\0\0\0\1\0\0\0\1\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\1\0\0\0\2\0\0\0\3\0\0\0\4\0\0\0\6\0\0\0\10\0\0\0\12\0\0\0\13\0"
+ "\0\0\15\0\0\0\15\0\0\0\14\0\0\0\13\0\0\0\11\0\0\0\7\0\0\0\5\0\0\0\4\0"
+ "\0\0\2\0\0\0\1\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\1\0\0\0\2\0\0\0\3\0\0\0\5\0\0\0\10\0\0\0\13\0\0\0\16"
+ "\0\0\0\22\0\0\0\24\0\0\0\26\0\0\0\26\0\0\0\26\0\0\0\23\0\0\0\21\0\0\0"
+ "\15\0\0\0\12\0\0\0\7\0\0\0\5\0\0\0\2\0\0\0\1\0\0\0\1\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\2\0\0\0\3\0\0\0\6\0\0\0\11\0\0"
+ "\0\16\0\0\0\23\0\0\0\30\0\0\0\35\0\0\0\40\0\0\0#\0\0\0#\0\0\0\"\0\0\0"
+ "\37\0\0\0\33\0\0\0\26\0\0\0\21\0\0\0\14\0\0\0\10\0\0\0\5\0\0\0\2\0\0"
+ "\0\1\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0\3\0\0\0\5"
+ "\0\0\0\12\0\0\0\17\0\0\0\26\0\0\0\35\0\0\0$\0\0\0+\0\0\0""0\0\0\0""4"
+ "\0\0\0""4\0\0\0""2\0\0\0.\0\0\0)\0\0\0\"\0\0\0\32\0\0\0\23\0\0\0\15\0"
+ "\0\0\10\0\0\0\5\0\0\0\2\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0"
+ "\2\0\0\0\4\0\0\0\10\0\0\0\16\0\0\0\26\0\0\0\37\0\0\0(\0\0\0""2\0\0\0"
+ ";\0\0\0A\0\0\0F\0\0\0G\0\0\0D\0\0\0@\0\0\0""8\0\0\0/\0\0\0%\0\0\0\34"
+ "\0\0\0\23\0\0\0\14\0\0\0\7\0\0\0\4\0\0\0\2\0\0\0\1\0\0\0\0\0\0\0\1\0"
+ "\0\0\1\0\0\0\3\0\0\0\7\0\0\0\13\0\0\0\23\0\0\0\35\0\0\0)\0\0\0""5\0\0"
+ "\0A\0\0\0L\0\0\0S\0\0\0X\0\0\0Y\0\0\0W\0\0\0Q\0\0\0I\0\0\0=\0\0\0""1"
+ "\0\0\0%\0\0\0\32\0\0\0\21\0\0\0\12\0\0\0\5\0\0\0\2\0\0\0\1\0\0\0\0\0"
+ "\0\0\1\0\0\0\2\0\0\0\4\0\0\0\10\0\0\0\17\0\0\0\31\0\0\0%\0\0\0""3\0\0"
+ "\0A\0\0\0P\0\0\0[\0\0\0d\0\0\0i\0\0\0k\0\0\0h\0\0\0b\0\0\0X\0\0\0L\0"
+ "\0\0=\0\0\0/\0\0\0\"\0\0\0\26\0\0\0\15\0\0\0\7\0\0\0\4\0\0\0\2\0\0\0"
+ "\1\0\0\0\1\0\0\0\2\0\0\0\5\0\0\0\13\0\0\0\23\0\0\0\36\0\0\0,\0\0\0=\0"
+ "\0\0M\0\0\0\\\0\0\0i\0\0\0r\0\0\0w\0\0\0y\0\0\0v\0\0\0p\0\0\0e\0\0\0"
+ "X\0\0\0I\0\0\0""8\0\0\0)\0\0\0\33\0\0\0\21\0\0\0\12\0\0\0\5\0\0\0\2\0"
+ "\0\0\1\0\0\0\1\0\0\0\3\0\0\0\7\0\0\0\15\0\0\0\26\0\0\0#\0\0\0""3\0\0"
+ "\0D\0\0\0V\0\0\0f\0\0\0s\0\0\0|\0\0\0\202\0\0\0\203\0\0\0\200\0\0\0z"
+ "\0\0\0p\0\0\0b\0\0\0R\0\0\0@\0\0\0/\0\0\0\40\0\0\0\24\0\0\0\13\0\0\0"
+ "\6\0\0\0\2\0\0\0\1\0\0\0\1\0\0\0\4\0\0\0\7\0\0\0\16\0\0\0\31\0\0\0'\0"
+ "\0\0""8\0\0\0K\0\0\0^\0\0\0n\0\0\0{\0\0\0\204\0\0\0\211\0\0\0\213\0\0"
+ "\0\210\0\0\0\202\0\0\0x\0\0\0k\0\0\0Z\0\0\0G\0\0\0""5\0\0\0%\0\0\0\27"
+ "\0\0\0\15\0\0\0\7\0\0\0\3\0\0\0\1\0\0\0\1\0\0\0\4\0\0\0\10\0\0\0\20\0"
+ "\0\0\33\0\0\0*\0\0\0<\0\0\0O\0\0\0b\0\0\0s\0\0\0\200\0\0\0\211\0\0\0"
+ "\216\0\0\0\217\0\0\0\215\0\0\0\210\0\0\0~\0\0\0p\0\0\0_\0\0\0M\0\0\0"
+ "9\0\0\0(\0\0\0\31\0\0\0\16\0\0\0\10\0\0\0\4\0\0\0\1\0\0\0\1\0\0\0\3\0"
+ "\0\0\7\0\0\0\16\0\0\0\27\0\0\0%\0\0\0""6\0\0\0I\0\0\0[\0\0\0k\0\0\0y"
+ "\0\0\0\202\0\0\0\210\0\0\0\211\0\0\0\210\0\0\0\202\0\0\0y\0\0\0k\0\0"
+ "\0[\0\0\0I\0\0\0""6\0\0\0%\0\0\0\27\0\0\0\16\0\0\0\7\0\0\0\3\0\0\0\1"
+ "\0\0\0\1\0\0\0\2\0\0\0\6\0\0\0\14\0\0\0\25\0\0\0!\0\0\0""1\0\0\0A\0\0"
+ "\0S\0\0\0c\0\0\0p\0\0\0z\0\0\0\200\0\0\0\202\0\0\0\200\0\0\0z\0\0\0p"
+ "\0\0\0c\0\0\0S\0\0\0A\0\0\0""0\0\0\0!\0\0\0\24\0\0\0\14\0\0\0\6\0\0\0"
+ "\3\0\0\0\1\0\0\0\1\0\0\0\2\0\0\0\5\0\0\0\12\0\0\0\21\0\0\0\34\0\0\0)"
+ "\0\0\0""9\0\0\0I\0\0\0X\0\0\0e\0\0\0n\0\0\0t\0\0\0v\0\0\0t\0\0\0n\0\0"
+ "\0e\0\0\0X\0\0\0I\0\0\0""9\0\0\0)\0\0\0\34\0\0\0\21\0\0\0\12\0\0\0\5"
+ "\0\0\0\2\0\0\0\1\0\0\0\1\0\0\0\2\0\0\0\4\0\0\0\10\0\0\0\16\0\0\0\27\0"
+ "\0\0\"\0\0\0/\0\0\0=\0\0\0K\0\0\0W\0\0\0`\0\0\0e\0\0\0h\0\0\0e\0\0\0"
+ "`\0\0\0W\0\0\0K\0\0\0=\0\0\0/\0\0\0\"\0\0\0\27\0\0\0\16\0\0\0\10\0\0"
+ "\0\4\0\0\0\2\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\2\0\0\0\5\0\0\0\12\0\0\0\21"
+ "\0\0\0\32\0\0\0%\0\0\0""1\0\0\0=\0\0\0G\0\0\0O\0\0\0U\0\0\0V\0\0\0U\0"
+ "\0\0O\0\0\0G\0\0\0=\0\0\0""1\0\0\0%\0\0\0\32\0\0\0\21\0\0\0\12\0\0\0"
+ "\5\0\0\0\2\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\2\0\0\0\4\0\0\0\7\0"
+ "\0\0\14\0\0\0\23\0\0\0\34\0\0\0%\0\0\0.\0\0\0""7\0\0\0>\0\0\0B\0\0\0"
+ "C\0\0\0B\0\0\0>\0\0\0""7\0\0\0.\0\0\0%\0\0\0\34\0\0\0\23\0\0\0\14\0\0"
+ "\0\7\0\0\0\4\0\0\0\2\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\2"
+ "\0\0\0\5\0\0\0\10\0\0\0\15\0\0\0\23\0\0\0\32\0\0\0!\0\0\0(\0\0\0,\0\0"
+ "\0""0\0\0\0""1\0\0\0""0\0\0\0,\0\0\0(\0\0\0!\0\0\0\32\0\0\0\23\0\0\0"
+ "\15\0\0\0\10\0\0\0\5\0\0\0\2\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\1\0\0\0\1\0\0\0\2\0\0\0\5\0\0\0\10\0\0\0\14\0\0\0\20\0\0\0\26"
+ "\0\0\0\32\0\0\0\36\0\0\0\40\0\0\0!\0\0\0\40\0\0\0\36\0\0\0\32\0\0\0\26"
+ "\0\0\0\20\0\0\0\14\0\0\0\10\0\0\0\5\0\0\0\2\0\0\0\1\0\0\0\1\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0\2\0\0\0\4\0\0\0"
+ "\7\0\0\0\12\0\0\0\15\0\0\0\20\0\0\0\23\0\0\0\24\0\0\0\24\0\0\0\24\0\0"
+ "\0\23\0\0\0\20\0\0\0\15\0\0\0\12\0\0\0\7\0\0\0\4\0\0\0\2\0\0\0\1\0\0"
+ "\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1"
+ "\0\0\0\1\0\0\0\2\0\0\0\4\0\0\0\5\0\0\0\7\0\0\0\10\0\0\0\12\0\0\0\13\0"
+ "\0\0\13\0\0\0\13\0\0\0\12\0\0\0\10\0\0\0\7\0\0\0\5\0\0\0\4\0\0\0\2\0"
+ "\0\0\1\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0\2\0\0\0\2\0\0\0\4\0\0\0\4\0"
+ "\0\0\5\0\0\0\5\0\0\0\6\0\0\0\5\0\0\0\5\0\0\0\4\0\0\0\4\0\0\0\2\0\0\0"
+ "\2\0\0\0\1\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0"
+ "\1\0\0\0\2\0\0\0\2\0\0\0\2\0\0\0\2\0\0\0\2\0\0\0\2\0\0\0\2\0\0\0\1\0"
+ "\0\0\1\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0"
+ "\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0"
+};
+
+static extents _win_extents = { 6, 6, 21, 6 };
+
+static quad _win_quads[] = {
+ {
+ { -LEFT_SPACE, -TOP_SPACE, GRAVITY_NORTH | GRAVITY_WEST },
+ { 0, 0, GRAVITY_NORTH | GRAVITY_WEST },
+ SHRT_MAX, SHRT_MAX,
+ 0,
+ {
+ 1.0, 0.0,
+ 0.0, 1.0,
+ 0.0, 0.0
+ }
+ }, {
+ { 0, -TOP_SPACE, GRAVITY_NORTH | GRAVITY_WEST },
+ { 0, 0, GRAVITY_NORTH | GRAVITY_EAST },
+ SHRT_MAX, SHRT_MAX,
+ ALIGN_LEFT,
+ {
+ 0.0, 0.0,
+ 0.0, 1.0,
+ LEFT_SPACE + 1, 0.0
+ }
+ }, {
+ { 0, -TOP_SPACE, GRAVITY_NORTH | GRAVITY_EAST },
+ { RIGHT_SPACE, 0, GRAVITY_NORTH | GRAVITY_EAST },
+ SHRT_MAX, SHRT_MAX,
+ 0,
+ {
+ 1.0, 0.0,
+ 0.0, 1.0,
+ LEFT_SPACE + 1.0, 0.0
+ }
+ },
+
+ {
+ { -LEFT_SPACE, 0, GRAVITY_NORTH | GRAVITY_WEST },
+ { 0, 0, GRAVITY_SOUTH | GRAVITY_WEST },
+ SHRT_MAX, SHRT_MAX,
+ 0,
+ {
+ 1.0, 0.0,
+ 0.0, 0.0,
+ 0.0, TOP_SPACE + 1.0
+ }
+ }, {
+
+ { 0, 0, GRAVITY_NORTH | GRAVITY_EAST },
+ { RIGHT_SPACE, 0, GRAVITY_SOUTH | GRAVITY_EAST },
+ SHRT_MAX, SHRT_MAX,
+ 0,
+ {
+ 1.0, 0.0,
+ 0.0, 0.0,
+ LEFT_SPACE + 1.0, TOP_SPACE + 1.0
+ }
+ },
+
+ {
+ { -LEFT_SPACE, 0, GRAVITY_SOUTH | GRAVITY_WEST },
+ { 0, 16, GRAVITY_SOUTH | GRAVITY_WEST },
+ SHRT_MAX, SHRT_MAX,
+ 0,
+ {
+ 1.0, 0.0,
+ 0.0, 1.0,
+ 0.0, TOP_SPACE + 1
+ }
+ }, {
+ { 0, 0, GRAVITY_SOUTH | GRAVITY_WEST },
+ { 0, BOTTOM_SPACE, GRAVITY_SOUTH | GRAVITY_EAST },
+ SHRT_MAX, SHRT_MAX,
+ 0,
+ {
+ 0.0, 0.0,
+ 0.0, 1.0,
+ LEFT_SPACE, TOP_SPACE + 1
+ }
+ }, {
+ { 0, 0, GRAVITY_SOUTH | GRAVITY_EAST },
+ { RIGHT_SPACE, BOTTOM_SPACE, GRAVITY_SOUTH | GRAVITY_EAST },
+ SHRT_MAX, SHRT_MAX,
+ 0,
+ {
+ 1.0, 0.0,
+ 0.0, 1.0,
+ LEFT_SPACE + 1, TOP_SPACE + 1
+ }
+ }
+};
+
+static quad _win_button_quads[] = {
+ {
+ { 0 /* + title width */, -TOP_SPACE, GRAVITY_NORTH | GRAVITY_WEST },
+ { -BUTTON_SPACE, 0, GRAVITY_NORTH | GRAVITY_EAST },
+ SHRT_MAX, SHRT_MAX,
+ 0,
+ {
+ 0.0, 0.0,
+ 0.0, 1.0,
+ 0.0 /* title width */, 0.0
+ }
+ }, {
+ { 0 /* title width + 1 */, -TOP_SPACE, GRAVITY_NORTH | GRAVITY_WEST },
+ { 0, 0, GRAVITY_NORTH | GRAVITY_EAST },
+ BUTTON_SPACE, SHRT_MAX,
+ ALIGN_RIGHT,
+ {
+ 1.0, 0.0,
+ 0.0, 1.0,
+ 0.0 /* title width + 1.0 */, 0.0
+ }
+ }
+};
+
+static GdkPixmap *shadow_pixmap = NULL;
+static GdkPixmap *large_shadow_pixmap = NULL;
+static GdkPixmap *decor_normal_pixmap = NULL;
+static GdkPixmap *decor_active_pixmap = NULL;
+
+static cairo_pattern_t *shadow_pattern;
+
+static Atom frame_window_atom;
+static Atom win_decor_atom;
+static Atom win_decor_sync_atom;
+static Atom wm_move_resize_atom;
+
+#define C(name) { 0, XC_ ## name }
+
+static struct _cursor {
+ Cursor cursor;
+ unsigned int shape;
+} cursor[3][3] = {
+ { C (top_left_corner), C (top_side), C (top_right_corner) },
+ { C (left_side), C (left_ptr), C (right_side) },
+ { C (bottom_left_corner), C (bottom_side), C (bottom_right_corner) }
+};
+
+static struct _pos {
+ int x, y, w, h;
+ int xw, yh, ww, hh;
+} pos[3][3] = {
+ {
+ { 0, 0, 10, 21, 0, 0, 0, 0 },
+ { 10, 0, -8, 6, 0, 0, 1, 0 },
+ { 2, 0, 10, 21, 1, 0, 0, 0 }
+ }, {
+ { 0, 10, 6, 11, 0, 0, 0, 1 },
+ { 6, 6, 0, 15, 0, 0, 1, 0 },
+ { 6, 10, 6, 11, 1, 0, 0, 1 }
+ }, {
+ { 0, 17, 10, 10, 0, 1, 0, 0 },
+ { 10, 21, -8, 6, 0, 1, 1, 0 },
+ { 2, 17, 10, 10, 1, 1, 0, 0 }
+ }
+}, bpos[3] = {
+ { -10, 6, 16, 16, 1, 0, 0, 0 },
+ { -26, 6, 16, 16, 1, 0, 0, 0 },
+ { -42, 6, 16, 16, 1, 0, 0, 0 }
+};
+
+typedef struct _decor_color {
+ double r;
+ double g;
+ double b;
+} decor_color_t;
+
+#define IN_EVENT_WINDOW (1 << 0)
+#define PRESSED_EVENT_WINDOW (1 << 1)
+
+typedef struct _decor {
+ Window event_windows[3][3];
+ Window button_windows[3];
+ guint button_states[3];
+ GdkPixmap *pixmap;
+ GdkPixmap *buffer_pixmap;
+ GdkGC *gc;
+ gint width;
+ gint height;
+ gboolean decorated;
+ gboolean active;
+ PangoLayout *layout;
+ gchar *name;
+ cairo_pattern_t *icon;
+ GdkPixmap *icon_pixmap;
+ WnckWindowState state;
+ WnckWindowActions actions;
+ XID prop_xid;
+} decor_t;
+
+typedef void (*event_callback) (WnckWindow *win, XEvent *event);
+
+static GtkWidget *style_window;
+
+static gboolean double_buffering = TRUE;
+static GHashTable *frame_table;
+static GtkWidget *action_menu = NULL;
+static gboolean action_menu_mapped = FALSE;
+static decor_color_t _title_color[2];
+static PangoContext *pango_context;
+static gint double_click_timeout = 250;
+
+static GtkWidget *tip_window;
+static GtkWidget *tip_label;
+static GTimeVal tooltip_last_popdown = { -1, -1 };
+static gint tooltip_timer_tag = 0;
+
+static GSList *draw_list = NULL;
+static guint draw_idle_id = 0;
+
+static void
+send_decor_sync_notify (decor_t *d)
+{
+ Display *xdisplay;
+ GdkDisplay *gdkdisplay;
+ GdkScreen *screen;
+ Window xroot;
+ XEvent ev;
+
+ gdkdisplay = gdk_display_get_default ();
+ xdisplay = GDK_DISPLAY_XDISPLAY (gdkdisplay);
+ screen = gdk_display_get_default_screen (gdkdisplay);
+ xroot = RootWindowOfScreen (gdk_x11_screen_get_xscreen (screen));
+
+ ev.xclient.type = ClientMessage;
+ ev.xclient.display = xdisplay;
+
+ ev.xclient.serial = 0;
+ ev.xclient.send_event = TRUE;
+
+ ev.xclient.window = xroot;
+ ev.xclient.message_type = win_decor_sync_atom;
+ ev.xclient.format = 32;
+
+ ev.xclient.data.l[0] = GDK_PIXMAP_XID (d->pixmap);
+ ev.xclient.data.l[1] = 0;
+ ev.xclient.data.l[2] = 0;
+ ev.xclient.data.l[3] = 0;
+ ev.xclient.data.l[4] = 0;
+
+ XSendEvent (xdisplay, xroot, FALSE,
+ SubstructureRedirectMask | SubstructureNotifyMask,
+ &ev);
+}
+
+/*
+ decoration property
+ -------------------
+
+ data[0] = pixmap
+
+ data[1] = input left
+ data[2] = input right
+ data[3] = input top
+ data[4] = input bottom
+
+ flags
+
+ 1st and 2nd bit p1 gravity, 3rd and 4th bit p2 gravity,
+ 5rd and 6th bit alignment, 7th bit XX, 8th bit XY, 9th bit YX, 10th bit YY.
+
+ data[4 + n * 9 + 1] = flags
+ data[4 + n * 9 + 2] = p1 x
+ data[4 + n * 9 + 3] = p1 y
+ data[4 + n * 9 + 4] = p2 x
+ data[4 + n * 9 + 5] = p2 y
+ data[4 + n * 9 + 6] = widthMax
+ data[4 + n * 9 + 7] = heightMax
+ data[4 + n * 9 + 8] = x0
+ data[4 + n * 9 + 9] = y0
+ */
+static void
+decoration_to_property (long *data,
+ Pixmap pixmap,
+ extents *input,
+ quad *quad,
+ int nQuad)
+{
+ memcpy (data++, &pixmap, sizeof (Pixmap));
+
+ *data++ = input->left;
+ *data++ = input->right;
+ *data++ = input->top;
+ *data++ = input->bottom;
+
+ while (nQuad--)
+ {
+ *data++ =
+ (quad->p1.gravity << 0) |
+ (quad->p2.gravity << 2) |
+ (quad->align << 4) |
+ (quad->m.xx ? XX_MASK : 0) |
+ (quad->m.xy ? XY_MASK : 0) |
+ (quad->m.yx ? YX_MASK : 0) |
+ (quad->m.yy ? YY_MASK : 0);
+
+ *data++ = quad->p1.x;
+ *data++ = quad->p1.y;
+ *data++ = quad->p2.x;
+ *data++ = quad->p2.y;
+ *data++ = quad->max_width;
+ *data++ = quad->max_height;
+ *data++ = quad->m.x0;
+ *data++ = quad->m.y0;
+
+ quad++;
+ }
+}
+
+static void
+decor_update_window_property (decor_t *d)
+{
+ long data[128];
+ Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
+ gint nQuad = sizeof (_win_quads) / sizeof (_win_quads[0]);
+ gint nButtonQuad = sizeof (_win_button_quads) /
+ sizeof (_win_button_quads[0]);
+ quad quads[nQuad + nButtonQuad];
+
+ memcpy (quads, _win_quads, nQuad * sizeof (quad));
+ memcpy (quads + nQuad, _win_button_quads, nButtonQuad * sizeof (quad));
+
+ quads[2].m.x0 = quads[4].m.x0 = quads[7].m.x0 = d->width - RIGHT_SPACE;
+ quads[1].m.xx = 1.0;
+
+ quads[1].max_width = d->width - LEFT_SPACE - RIGHT_SPACE - BUTTON_SPACE;
+
+ quads[8].p1.x = d->width - LEFT_SPACE - RIGHT_SPACE - BUTTON_SPACE;
+ quads[8].m.x0 = d->width - RIGHT_SPACE - BUTTON_SPACE;
+
+ quads[9].p1.x = d->width - LEFT_SPACE - RIGHT_SPACE - BUTTON_SPACE;
+ quads[9].m.x0 = d->width - RIGHT_SPACE - BUTTON_SPACE;
+
+ quads[9].max_width = BUTTON_SPACE;
+
+ nQuad += nButtonQuad;
+
+ decoration_to_property (data, GDK_PIXMAP_XID (d->pixmap),
+ &_win_extents, quads, nQuad);
+
+ gdk_error_trap_push ();
+ XChangeProperty (xdisplay, d->prop_xid,
+ win_decor_atom,
+ XA_INTEGER,
+ 32, PropModeReplace, (guchar *) data, 5 + 9 * nQuad);
+ XSync (xdisplay, FALSE);
+ gdk_error_trap_pop ();
+}
+
+static void
+gdk_cairo_set_source_color_alpha (cairo_t *cr,
+ GdkColor *color,
+ double alpha)
+{
+ cairo_set_source_rgba (cr,
+ color->red / 65535.0,
+ color->green / 65535.0,
+ color->blue / 65535.0,
+ alpha);
+}
+
+#define CORNER_TOPLEFT (1 << 0)
+#define CORNER_TOPRIGHT (1 << 1)
+#define CORNER_BOTTOMRIGHT (1 << 2)
+#define CORNER_BOTTOMLEFT (1 << 3)
+
+static void
+rounded_rectangle (cairo_t *cr,
+ double x,
+ double y,
+ double w,
+ double h,
+ double radius,
+ int corner)
+{
+ if (corner & CORNER_TOPLEFT)
+ cairo_move_to (cr, x + radius, y);
+ else
+ cairo_move_to (cr, x, y);
+
+ if (corner & CORNER_TOPRIGHT)
+ cairo_arc (cr, x + w - radius, y + radius, radius,
+ M_PI * 1.5, M_PI * 2.0);
+ else
+ cairo_line_to (cr, x + w, y);
+
+ if (corner & CORNER_BOTTOMRIGHT)
+ cairo_arc (cr, x + w - radius, y + h - radius, radius,
+ 0.0, M_PI * 0.5);
+ else
+ cairo_line_to (cr, x + w, y + h);
+
+ if (corner & CORNER_BOTTOMLEFT)
+ cairo_arc (cr, x + radius, y + h - radius, radius,
+ M_PI * 0.5, M_PI);
+ else
+ cairo_line_to (cr, x, y + h);
+
+ if (corner & CORNER_TOPLEFT)
+ cairo_arc (cr, x + radius, y + radius, radius, M_PI, M_PI * 1.5);
+ else
+ cairo_line_to (cr, x, y);
+}
+
+#define SHADE_LEFT (1 << 0)
+#define SHADE_RIGHT (1 << 1)
+#define SHADE_TOP (1 << 2)
+#define SHADE_BOTTOM (1 << 3)
+
+static void
+fill_rounded_rectangle (cairo_t *cr,
+ double x,
+ double y,
+ double w,
+ double h,
+ double radius,
+ int corner,
+ decor_color_t *c0,
+ double alpha0,
+ decor_color_t *c1,
+ double alpha1,
+ int gravity)
+{
+ cairo_pattern_t *pattern;
+
+ rounded_rectangle (cr, x, y, w, h, radius, corner);
+
+ if (gravity & SHADE_RIGHT)
+ {
+ x = x + w;
+ w = -w;
+ }
+ else if (!(gravity & SHADE_LEFT))
+ {
+ x = w = 0;
+ }
+
+ if (gravity & SHADE_BOTTOM)
+ {
+ y = y + h;
+ h = -h;
+ }
+ else if (!(gravity & SHADE_TOP))
+ {
+ y = h = 0;
+ }
+
+ if (w && h)
+ {
+ cairo_matrix_t matrix;
+
+ pattern = cairo_pattern_create_radial (0.0, 0.0, 0.0, 0.0, 0.0, w);
+
+ cairo_matrix_init_scale (&matrix, 1.0, w / h);
+ cairo_matrix_translate (&matrix, -(x + w), -(y + h));
+
+ cairo_pattern_set_matrix (pattern, &matrix);
+ }
+ else
+ {
+ pattern = cairo_pattern_create_linear (x + w, y + h, x, y);
+ }
+
+ cairo_pattern_add_color_stop_rgba (pattern, 0.0, c0->r, c0->g, c0->b,
+ alpha0);
+
+ cairo_pattern_add_color_stop_rgba (pattern, 1.0, c1->r, c1->g, c1->b,
+ alpha1);
+
+ cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD);
+
+ cairo_set_source (cr, pattern);
+ cairo_fill (cr);
+ cairo_pattern_destroy (pattern);
+}
+
+static void
+draw_shadow_background (decor_t *d,
+ cairo_t *cr)
+{
+ cairo_matrix_t matrix;
+ double w, h, x2, y2;
+
+ w = d->width - 13.0 - 14.0;
+ h = d->height - 13.0 - 14.0;
+
+ x2 = d->width - 14.0;
+ y2 = d->height - 14.0;
+
+ /* top left */
+ cairo_matrix_init_identity (&matrix);
+ cairo_pattern_set_matrix (shadow_pattern, &matrix);
+ cairo_set_source (cr, shadow_pattern);
+ cairo_rectangle (cr, 0.0, 0.0, 13.0, 13.0);
+ cairo_fill (cr);
+
+ /* top */
+ cairo_matrix_init_translate (&matrix, 13.0, 0.0);
+ cairo_matrix_scale (&matrix, 1.0 / w, 1.0);
+ cairo_matrix_translate (&matrix, -13.0, 0.0);
+ cairo_pattern_set_matrix (shadow_pattern, &matrix);
+ cairo_set_source (cr, shadow_pattern);
+ cairo_rectangle (cr, 13.0, 0.0, w, 13.0);
+ cairo_fill (cr);
+
+ /* top right */
+ cairo_matrix_init_translate (&matrix, 13.0 - x2, 0.0);
+ cairo_pattern_set_matrix (shadow_pattern, &matrix);
+ cairo_set_source (cr, shadow_pattern);
+ cairo_rectangle (cr, x2, 0.0, 14.0, 13.0);
+ cairo_fill (cr);
+
+ /* left */
+ cairo_matrix_init_translate (&matrix, 0.0, 13.0);
+ cairo_matrix_scale (&matrix, 1.0, 1.0 / h);
+ cairo_matrix_translate (&matrix, 0.0, -13.0);
+ cairo_pattern_set_matrix (shadow_pattern, &matrix);
+ cairo_set_source (cr, shadow_pattern);
+ cairo_rectangle (cr, 0.0, 13.0, 13.0, h);
+ cairo_fill (cr);
+
+ /* right */
+ cairo_matrix_init_translate (&matrix, 13.0 - x2, 13.0);
+ cairo_matrix_scale (&matrix, 1.0, 1.0 / h);
+ cairo_matrix_translate (&matrix, 0.0, -13.0);
+ cairo_pattern_set_matrix (shadow_pattern, &matrix);
+ cairo_set_source (cr, shadow_pattern);
+ cairo_rectangle (cr, x2, 13.0, 14.0, h);
+ cairo_fill (cr);
+
+ /* bottom right */
+ cairo_matrix_init_translate (&matrix, 0.0, -15.0);
+ cairo_pattern_set_matrix (shadow_pattern, &matrix);
+ cairo_set_source (cr, shadow_pattern);
+ cairo_rectangle (cr, 0.0, y2, 13.0, 14.0);
+ cairo_fill (cr);
+
+ /* bottom */
+ cairo_matrix_init_translate (&matrix, 13.0, -15.0);
+ cairo_matrix_scale (&matrix, 1.0 / w, 1.0);
+ cairo_matrix_translate (&matrix, -13.0, 0.0);
+ cairo_pattern_set_matrix (shadow_pattern, &matrix);
+ cairo_set_source (cr, shadow_pattern);
+ cairo_rectangle (cr, 13.0, y2, w, 14.0);
+ cairo_fill (cr);
+
+ /* right */
+ cairo_matrix_init_translate (&matrix, 13.0 - x2, 13.0 - y2);
+ cairo_pattern_set_matrix (shadow_pattern, &matrix);
+ cairo_set_source (cr, shadow_pattern);
+ cairo_rectangle (cr, x2, y2, 14.0, 14.0);
+ cairo_fill (cr);
+}
+
+static void
+draw_close_button (decor_t *d,
+ cairo_t *cr,
+ double s)
+{
+ cairo_rel_move_to (cr, 0.0, s);
+
+ cairo_rel_line_to (cr, s, -s);
+ cairo_rel_line_to (cr, s, s);
+ cairo_rel_line_to (cr, s, -s);
+ cairo_rel_line_to (cr, s, s);
+
+ cairo_rel_line_to (cr, -s, s);
+ cairo_rel_line_to (cr, s, s);
+ cairo_rel_line_to (cr, -s, s);
+ cairo_rel_line_to (cr, -s, -s);
+
+ cairo_rel_line_to (cr, -s, s);
+ cairo_rel_line_to (cr, -s, -s);
+ cairo_rel_line_to (cr, s, -s);
+
+ cairo_close_path (cr);
+}
+
+static void
+draw_max_button (decor_t *d,
+ cairo_t *cr,
+ double s)
+{
+ cairo_rel_line_to (cr, 12.0, 0.0);
+ cairo_rel_line_to (cr, 0.0, 12.0);
+ cairo_rel_line_to (cr, -12.0, 0.0);
+
+ cairo_close_path (cr);
+
+ cairo_rel_move_to (cr, 2.0, s);
+
+ cairo_rel_line_to (cr, 12.0 - 4.0, 0.0);
+ cairo_rel_line_to (cr, 0.0, 12.0 - s - 2.0);
+ cairo_rel_line_to (cr, -(12.0 - 4.0), 0.0);
+
+ cairo_close_path (cr);
+}
+
+static void
+draw_unmax_button (decor_t *d,
+ cairo_t *cr,
+ double s)
+{
+ cairo_rel_move_to (cr, 1.0, 1.0);
+
+ cairo_rel_line_to (cr, 10.0, 0.0);
+ cairo_rel_line_to (cr, 0.0, 10.0);
+ cairo_rel_line_to (cr, -10.0, 0.0);
+
+ cairo_close_path (cr);
+
+ cairo_rel_move_to (cr, 2.0, s);
+
+ cairo_rel_line_to (cr, 10.0 - 4.0, 0.0);
+ cairo_rel_line_to (cr, 0.0, 10.0 - s - 2.0);
+ cairo_rel_line_to (cr, -(10.0 - 4.0), 0.0);
+
+ cairo_close_path (cr);
+}
+
+static void
+draw_min_button (decor_t *d,
+ cairo_t *cr,
+ double s)
+{
+ cairo_rel_move_to (cr, 0.0, 8.0);
+
+ cairo_rel_line_to (cr, 12.0, 0.0);
+ cairo_rel_line_to (cr, 0.0, s);
+ cairo_rel_line_to (cr, -12.0, 0.0);
+
+ cairo_close_path (cr);
+}
+
+static void
+button_state_offsets (gdouble x,
+ gdouble y,
+ guint state,
+ gdouble *return_x,
+ gdouble *return_y,
+ gdouble *return_sx,
+ gdouble *return_sy)
+{
+ static double off[] = { 0.0, 0.0, 0.0, 1.0 };
+ static double shadow_off[] = { 1.0, 1.0, 1.0, 0.0 };
+
+ *return_x = x + off[state];
+ *return_y = y + off[state];
+ *return_sx = x + shadow_off[state];
+ *return_sy = y + shadow_off[state];
+}
+
+static void
+draw_window_decoration (decor_t *d)
+{
+ cairo_t *cr;
+ GtkStyle *style;
+ decor_color_t color;
+ double alpha;
+ double x1, y1, x2, y2, x, y, sx, sy;
+ int corners = SHADE_LEFT | SHADE_RIGHT | SHADE_TOP | SHADE_BOTTOM;
+
+ if (!d->pixmap)
+ return;
+
+ style = gtk_widget_get_style (style_window);
+
+ if (d->state & (WNCK_WINDOW_STATE_MAXIMIZED_HORIZONTALLY |
+ WNCK_WINDOW_STATE_MAXIMIZED_VERTICALLY))
+ corners = 0;
+
+ color.r = style->bg[GTK_STATE_NORMAL].red / 65535.0;
+ color.g = style->bg[GTK_STATE_NORMAL].green / 65535.0;
+ color.b = style->bg[GTK_STATE_NORMAL].blue / 65535.0;
+
+ if (d->buffer_pixmap)
+ cr = gdk_cairo_create (GDK_DRAWABLE (d->buffer_pixmap));
+ else
+ cr = gdk_cairo_create (GDK_DRAWABLE (d->pixmap));
+
+ cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+
+ x1 = LEFT_SPACE - _win_extents.left;
+ y1 = TOP_SPACE - _win_extents.top;
+ x2 = d->width - RIGHT_SPACE + _win_extents.right;
+ y2 = d->height - BOTTOM_SPACE + _win_extents.bottom;
+
+ cairo_set_line_width (cr, 1.0);
+
+ draw_shadow_background (d, cr);
+
+ if (d->active)
+ {
+ decor_color_t *title_color = _title_color;
+
+ alpha = 0.8;
+
+ fill_rounded_rectangle (cr,
+ x1 + 0.5,
+ y1 + 0.5,
+ _win_extents.left - 0.5,
+ _win_extents.top - 0.5,
+ 5.0, CORNER_TOPLEFT & corners,
+ &title_color[0], 1.0, &title_color[1], alpha,
+ SHADE_TOP | SHADE_LEFT);
+
+ fill_rounded_rectangle (cr,
+ x1 + _win_extents.left,
+ y1 + 0.5,
+ x2 - x1 - _win_extents.left -
+ _win_extents.right,
+ _win_extents.top - 0.5,
+ 5.0, 0,
+ &title_color[0], 1.0, &title_color[1], alpha,
+ SHADE_TOP);
+
+ fill_rounded_rectangle (cr,
+ x2 - _win_extents.right,
+ y1 + 0.5,
+ _win_extents.right - 0.5,
+ _win_extents.top - 0.5,
+ 5.0, CORNER_TOPRIGHT & corners,
+ &title_color[0], 1.0, &title_color[1], alpha,
+ SHADE_TOP | SHADE_RIGHT);
+ }
+ else
+ {
+ alpha = 0.5;
+
+ fill_rounded_rectangle (cr,
+ x1 + 0.5,
+ y1 + 0.5,
+ _win_extents.left - 0.5,
+ _win_extents.top - 0.5,
+ 5.0, CORNER_TOPLEFT & corners,
+ &color, 1.0, &color, alpha,
+ SHADE_TOP | SHADE_LEFT);
+
+ fill_rounded_rectangle (cr,
+ x1 + _win_extents.left,
+ y1 + 0.5,
+ x2 - x1 - _win_extents.left -
+ _win_extents.right,
+ _win_extents.top - 0.5,
+ 5.0, 0,
+ &color, 1.0, &color, alpha,
+ SHADE_TOP);
+
+ fill_rounded_rectangle (cr,
+ x2 - _win_extents.right,
+ y1 + 0.5,
+ _win_extents.right - 0.5,
+ _win_extents.top - 0.5,
+ 5.0, CORNER_TOPRIGHT & corners,
+ &color, 1.0, &color, alpha,
+ SHADE_TOP | SHADE_RIGHT);
+ }
+
+ fill_rounded_rectangle (cr,
+ x1 + 0.5,
+ y1 + _win_extents.top,
+ _win_extents.left - 0.5,
+ 1,
+ 5.0, 0,
+ &color, 1.0, &color, alpha,
+ SHADE_LEFT);
+
+ fill_rounded_rectangle (cr,
+ x2 - _win_extents.right,
+ y1 + _win_extents.top,
+ _win_extents.right - 0.5,
+ 1,
+ 5.0, 0,
+ &color, 1.0, &color, alpha,
+ SHADE_RIGHT);
+
+
+ fill_rounded_rectangle (cr,
+ x1 + 0.5,
+ y2 - _win_extents.bottom,
+ _win_extents.left - 0.5,
+ _win_extents.bottom - 0.5,
+ 5.0, CORNER_BOTTOMLEFT & corners,
+ &color, 1.0, &color, alpha,
+ SHADE_BOTTOM | SHADE_LEFT);
+
+ fill_rounded_rectangle (cr,
+ x1 + _win_extents.left,
+ y2 - _win_extents.bottom,
+ x2 - x1 - _win_extents.left -
+ _win_extents.right,
+ _win_extents.bottom - 0.5,
+ 5.0, 0,
+ &color, 1.0, &color, alpha,
+ SHADE_BOTTOM);
+
+ fill_rounded_rectangle (cr,
+ x2 - _win_extents.right,
+ y2 - _win_extents.bottom,
+ _win_extents.right - 0.5,
+ _win_extents.bottom - 0.5,
+ 5.0, CORNER_BOTTOMRIGHT & corners,
+ &color, 1.0, &color, alpha,
+ SHADE_BOTTOM | SHADE_RIGHT);
+
+ cairo_rectangle (cr,
+ LEFT_SPACE, TOP_SPACE,
+ d->width - LEFT_SPACE - RIGHT_SPACE, 1);
+ gdk_cairo_set_source_color (cr, &style->bg[GTK_STATE_NORMAL]);
+ cairo_fill (cr);
+
+ cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+
+ if (d->active)
+ {
+ gdk_cairo_set_source_color_alpha (cr,
+ &style->fg[GTK_STATE_NORMAL],
+ 0.7);
+
+ cairo_move_to (cr, x1 + 0.5, y1 + _win_extents.top - 0.5);
+ cairo_rel_line_to (cr, x2 - x1 - 1.0, 0.0);
+
+ cairo_stroke (cr);
+ }
+
+ rounded_rectangle (cr,
+ x1 + 0.5, y1 + 0.5,
+ x2 - x1 - 1.0, y2 - y1 - 1.0,
+ 5.0,
+ (CORNER_TOPLEFT | CORNER_TOPRIGHT | CORNER_BOTTOMLEFT |
+ CORNER_BOTTOMRIGHT) & corners);
+
+ cairo_clip (cr);
+
+ cairo_translate (cr, 1.0, 1.0);
+
+ rounded_rectangle (cr,
+ x1 + 0.5, y1 + 0.5,
+ x2 - x1 - 1.0, y2 - y1 - 1.0,
+ 5.0,
+ (CORNER_TOPLEFT | CORNER_TOPRIGHT | CORNER_BOTTOMLEFT |
+ CORNER_BOTTOMRIGHT) & corners);
+
+ cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.4);
+ cairo_stroke (cr);
+
+ cairo_translate (cr, -2.0, -2.0);
+
+ rounded_rectangle (cr,
+ x1 + 0.5, y1 + 0.5,
+ x2 - x1 - 1.0, y2 - y1 - 1.0,
+ 5.0,
+ (CORNER_TOPLEFT | CORNER_TOPRIGHT | CORNER_BOTTOMLEFT |
+ CORNER_BOTTOMRIGHT) & corners);
+
+ cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.1);
+ cairo_stroke (cr);
+
+ cairo_translate (cr, 1.0, 1.0);
+
+ cairo_reset_clip (cr);
+
+ rounded_rectangle (cr,
+ x1 + 0.5, y1 + 0.5,
+ x2 - x1 - 1.0, y2 - y1 - 1.0,
+ 5.0,
+ (CORNER_TOPLEFT | CORNER_TOPRIGHT | CORNER_BOTTOMLEFT |
+ CORNER_BOTTOMRIGHT) & corners);
+
+ gdk_cairo_set_source_color_alpha (cr,
+ &style->fg[GTK_STATE_NORMAL],
+ alpha);
+
+ cairo_stroke (cr);
+
+ if (d->actions & WNCK_WINDOW_ACTION_CLOSE)
+ {
+ button_state_offsets (d->width - RIGHT_SPACE - BUTTON_SPACE + 39.0,
+ 11.0, d->button_states[0], &x, &y, &sx, &sy);
+
+ if (d->active)
+ {
+ gdk_cairo_set_source_color_alpha (cr,
+ &style->fg[GTK_STATE_NORMAL],
+ alpha);
+ cairo_move_to (cr, sx, sy);
+ draw_close_button (d, cr, 3.0);
+ cairo_fill (cr);
+
+ if (d->button_states[0] & IN_EVENT_WINDOW)
+ cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
+ else
+ cairo_set_source_rgba (cr, color.r, color.g, color.b, 0.95);
+
+ cairo_move_to (cr, x, y);
+ draw_close_button (d, cr, 3.0);
+ cairo_fill (cr);
+ }
+ else
+ {
+ gdk_cairo_set_source_color_alpha (cr,
+ &style->fg[GTK_STATE_NORMAL],
+ alpha * 0.75);
+ cairo_move_to (cr, x, y);
+ draw_close_button (d, cr, 3.0);
+ cairo_fill (cr);
+ }
+ }
+
+ if (d->actions & WNCK_WINDOW_ACTION_MAXIMIZE)
+ {
+ button_state_offsets (d->width - RIGHT_SPACE - BUTTON_SPACE + 21.0,
+ 11.0, d->button_states[1], &x, &y, &sx, &sy);
+
+ cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
+
+ if (d->active)
+ {
+ gdk_cairo_set_source_color_alpha (cr,
+ &style->fg[GTK_STATE_NORMAL],
+ alpha);
+ cairo_move_to (cr, sx, sy);
+
+ if (d->state & (WNCK_WINDOW_STATE_MAXIMIZED_HORIZONTALLY |
+ WNCK_WINDOW_STATE_MAXIMIZED_VERTICALLY))
+ draw_unmax_button (d, cr, 4.0);
+ else
+ draw_max_button (d, cr, 4.0);
+
+ cairo_fill (cr);
+
+ if (d->button_states[1] & IN_EVENT_WINDOW)
+ cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
+ else
+ cairo_set_source_rgba (cr, color.r, color.g, color.b, 0.95);
+
+ cairo_move_to (cr, x, y);
+
+ if (d->state & (WNCK_WINDOW_STATE_MAXIMIZED_HORIZONTALLY |
+ WNCK_WINDOW_STATE_MAXIMIZED_VERTICALLY))
+ draw_unmax_button (d, cr, 4.0);
+ else
+ draw_max_button (d, cr, 4.0);
+
+ cairo_fill (cr);
+ }
+ else
+ {
+ gdk_cairo_set_source_color_alpha (cr,
+ &style->fg[GTK_STATE_NORMAL],
+ alpha * 0.75);
+ cairo_move_to (cr, x, y);
+
+ if (d->state & (WNCK_WINDOW_STATE_MAXIMIZED_HORIZONTALLY |
+ WNCK_WINDOW_STATE_MAXIMIZED_VERTICALLY))
+ draw_unmax_button (d, cr, 4.0);
+ else
+ draw_max_button (d, cr, 4.0);
+
+ cairo_fill (cr);
+ }
+ }
+
+ if (d->actions & WNCK_WINDOW_ACTION_MINIMIZE)
+ {
+ button_state_offsets (d->width - RIGHT_SPACE - BUTTON_SPACE + 3.0,
+ 11.0, d->button_states[2], &x, &y, &sx, &sy);
+
+ if (d->active)
+ {
+ gdk_cairo_set_source_color_alpha (cr,
+ &style->fg[GTK_STATE_NORMAL],
+ alpha);
+ cairo_move_to (cr, sx, sy);
+ draw_min_button (d, cr, 4.0);
+ cairo_fill (cr);
+
+ if (d->button_states[2] & IN_EVENT_WINDOW)
+ cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
+ else
+ cairo_set_source_rgba (cr, color.r, color.g, color.b, 0.95);
+
+ cairo_move_to (cr, x, y);
+ draw_min_button (d, cr, 4.0);
+ cairo_fill (cr);
+ }
+ else
+ {
+ gdk_cairo_set_source_color_alpha (cr,
+ &style->fg[GTK_STATE_NORMAL],
+ alpha * 0.75);
+ cairo_move_to (cr, x, y);
+ draw_min_button (d, cr, 4.0);
+ cairo_fill (cr);
+ }
+ }
+
+ if (d->layout)
+ {
+ gdk_cairo_set_source_color_alpha (cr,
+ &style->fg[GTK_STATE_NORMAL],
+ alpha);
+
+ if (d->active)
+ {
+ cairo_move_to (cr, 33.0, 9.0);
+
+ pango_cairo_show_layout (cr, d->layout);
+
+ cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
+ }
+
+ cairo_move_to (cr, 32.0, 8.0);
+
+ pango_cairo_show_layout (cr, d->layout);
+ }
+
+ if (d->icon)
+ {
+ cairo_translate (cr, LEFT_SPACE, 9.0);
+ cairo_set_source (cr, d->icon);
+ cairo_rectangle (cr, 0.0, 0.0, 16.0, 16.0);
+ cairo_clip (cr);
+
+ if (d->active)
+ cairo_paint (cr);
+ else
+ cairo_paint_with_alpha (cr, alpha);
+ }
+
+ cairo_destroy (cr);
+
+ if (d->buffer_pixmap)
+ gdk_draw_drawable (d->pixmap,
+ d->gc,
+ d->buffer_pixmap,
+ 0,
+ 0,
+ 0,
+ 0,
+ d->width,
+ d->height);
+
+ if (d->prop_xid)
+ {
+ decor_update_window_property (d);
+ d->prop_xid = 0;
+ }
+
+ send_decor_sync_notify (d);
+}
+
+static gboolean
+draw_decor_list (void *data)
+{
+ GSList *list;
+
+ draw_idle_id = 0;
+
+ for (list = draw_list; list; list = list->next)
+ draw_window_decoration ((decor_t *) list->data);
+
+ g_slist_free (draw_list);
+ draw_list = NULL;
+
+ return FALSE;
+}
+
+static void
+queue_decor_draw (decor_t *d)
+{
+ if (g_slist_find (draw_list, d))
+ return;
+
+ draw_list = g_slist_append (draw_list, d);
+
+ if (!draw_idle_id)
+ draw_idle_id = g_idle_add (draw_decor_list, NULL);
+}
+
+static GdkPixmap *
+create_pixmap (int w,
+ int h)
+{
+ GdkPixmap *pixmap;
+ GdkVisual *visual;
+ GdkColormap *colormap;
+
+ visual = gdk_visual_get_best_with_depth (32);
+ if (!visual)
+ return NULL;
+
+ pixmap = gdk_pixmap_new (NULL, w, h, 32);
+ if (!pixmap)
+ return NULL;
+
+ colormap = gdk_colormap_new (visual, FALSE);
+ if (!colormap)
+ {
+ gdk_pixmap_unref (pixmap);
+ return NULL;
+ }
+
+ gdk_drawable_set_colormap (GDK_DRAWABLE (pixmap), colormap);
+ gdk_colormap_unref (colormap);
+
+ return pixmap;
+}
+
+static GdkPixmap *
+pixmap_new_from_pixbuf (GdkPixbuf *pixbuf)
+{
+ GdkPixmap *pixmap;
+ guint width, height;
+ cairo_t *cr;
+
+ width = gdk_pixbuf_get_width (pixbuf);
+ height = gdk_pixbuf_get_height (pixbuf);
+
+ pixmap = create_pixmap (width, height);
+ if (!pixmap)
+ return NULL;
+
+ cr = (cairo_t *) gdk_cairo_create (GDK_DRAWABLE (pixmap));
+ gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
+ cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+ cairo_paint (cr);
+ cairo_destroy (cr);
+
+ return pixmap;
+}
+
+static GdkPixmap *
+pixmap_new_from_inline (const guint8 *data)
+{
+ GdkPixbuf *pixbuf;
+
+ pixbuf = gdk_pixbuf_new_from_inline (-1, data, FALSE, NULL);
+ if (!pixbuf)
+ return NULL;
+
+ return pixmap_new_from_pixbuf (pixbuf);
+}
+
+static void
+update_default_decorations (GdkScreen *screen)
+{
+ long data[128];
+ Window xroot;
+ GdkDisplay *gdkdisplay = gdk_display_get_default ();
+ Display *xdisplay = gdk_x11_display_get_xdisplay (gdkdisplay);
+ Atom atom;
+ int nQuad;
+ decor_t d;
+
+ xroot = RootWindowOfScreen (gdk_x11_screen_get_xscreen (screen));
+
+ atom = XInternAtom (xdisplay, "_NET_WINDOW_DECOR_BARE", FALSE);
+ nQuad = sizeof (_shadow_quads) / sizeof (_shadow_quads[0]);
+ decoration_to_property (data, GDK_PIXMAP_XID (shadow_pixmap),
+ &_shadow_extents, _shadow_quads, nQuad);
+
+ XChangeProperty (xdisplay, xroot,
+ atom,
+ XA_INTEGER,
+ 32, PropModeReplace, (guchar *) data, 5 + 9 * nQuad);
+
+ d.width = LEFT_SPACE + 1 + RIGHT_SPACE;
+ d.height = TOP_SPACE + 1 + BOTTOM_SPACE;
+
+ if (!decor_normal_pixmap)
+ decor_normal_pixmap = create_pixmap (d.width, d.height);
+
+ if (decor_normal_pixmap)
+ {
+ d.pixmap = decor_normal_pixmap;
+ d.buffer_pixmap = NULL;
+ d.active = FALSE;
+ d.layout = NULL;
+ d.icon = NULL;
+ d.state = 0;
+ d.actions = 0;
+ d.prop_xid = 0;
+
+ draw_window_decoration (&d);
+
+ atom = XInternAtom (xdisplay, "_NET_WINDOW_DECOR_NORMAL", FALSE);
+ nQuad = sizeof (_win_quads) / sizeof (_win_quads[0]);
+ decoration_to_property (data, GDK_PIXMAP_XID (d.pixmap),
+ &_win_extents, _win_quads, nQuad);
+
+ XChangeProperty (xdisplay, xroot,
+ atom,
+ XA_INTEGER,
+ 32, PropModeReplace, (guchar *) data, 5 + 9 * nQuad);
+ }
+
+ if (!decor_active_pixmap)
+ decor_active_pixmap = create_pixmap (d.width, d.height);
+
+ if (decor_active_pixmap)
+ {
+ d.pixmap = decor_active_pixmap;
+ d.buffer_pixmap = NULL;
+ d.active = TRUE;
+ d.layout = NULL;
+ d.icon = NULL;
+ d.state = 0;
+ d.actions = 0;
+ d.prop_xid = 0;
+
+ draw_window_decoration (&d);
+
+ atom = XInternAtom (xdisplay, "_NET_WINDOW_DECOR_ACTIVE", FALSE);
+ nQuad = sizeof (_win_quads) / sizeof (_win_quads[0]);
+ decoration_to_property (data, GDK_PIXMAP_XID (d.pixmap),
+ &_win_extents, _win_quads, nQuad);
+
+ XChangeProperty (xdisplay, xroot,
+ atom,
+ XA_INTEGER,
+ 32, PropModeReplace, (guchar *) data, 5 + 9 * nQuad);
+ }
+}
+
+static void
+set_dm_check_hint (GdkScreen *screen)
+{
+ XSetWindowAttributes attrs;
+ unsigned long data[1];
+ Window xroot;
+ GdkDisplay *gdkdisplay = gdk_display_get_default ();
+ Display *xdisplay = gdk_x11_display_get_xdisplay (gdkdisplay);
+ Atom atom;
+
+ attrs.override_redirect = TRUE;
+ attrs.event_mask = PropertyChangeMask;
+
+ xroot = RootWindowOfScreen (gdk_x11_screen_get_xscreen (screen));
+
+ data[0] = XCreateWindow (xdisplay,
+ xroot,
+ -100, -100, 1, 1,
+ 0,
+ CopyFromParent,
+ CopyFromParent,
+ (Visual *) CopyFromParent,
+ CWOverrideRedirect | CWEventMask,
+ &attrs);
+
+ atom = XInternAtom (xdisplay, "_NET_SUPPORTING_DM_CHECK", FALSE);
+
+ XChangeProperty (xdisplay, xroot,
+ atom,
+ XA_WINDOW,
+ 32, PropModeReplace, (guchar *) data, 1);
+}
+
+static gboolean
+check_dm_hint (GdkScreen *screen)
+{
+ Window xroot;
+ GdkDisplay *gdkdisplay = gdk_display_get_default ();
+ Display *xdisplay = gdk_x11_display_get_xdisplay (gdkdisplay);
+ Atom actual;
+ int result, format;
+ unsigned long n, left;
+ unsigned char *data;
+ Atom atom;
+ gboolean dm = FALSE;
+
+ xroot = RootWindowOfScreen (gdk_x11_screen_get_xscreen (screen));
+
+ atom = XInternAtom (xdisplay, "_NET_SUPPORTING_DM_CHECK", FALSE);
+
+ result = XGetWindowProperty (xdisplay, xroot,
+ atom, 0L, 1L, FALSE,
+ XA_WINDOW, &actual, &format,
+ &n, &left, &data);
+
+ if (result == Success && n && data)
+ {
+ XWindowAttributes attr;
+ Window window;
+
+ memcpy (&window, data, sizeof (Window));
+
+ XFree (data);
+
+ gdk_error_trap_push ();
+
+ XGetWindowAttributes (xdisplay, window, &attr);
+ XSync (xdisplay, FALSE);
+
+ if (!gdk_error_trap_pop ())
+ dm = TRUE;
+ }
+
+ return dm;
+}
+
+static gboolean
+get_window_prop (Window xwindow,
+ Atom atom,
+ Window *val)
+{
+ Atom type;
+ int format;
+ gulong nitems;
+ gulong bytes_after;
+ Window *w;
+ int err, result;
+
+ *val = 0;
+
+ gdk_error_trap_push ();
+
+ type = None;
+ result = XGetWindowProperty (gdk_display,
+ xwindow,
+ atom,
+ 0, G_MAXLONG,
+ False, XA_WINDOW, &type, &format, &nitems,
+ &bytes_after, (void*) &w);
+ err = gdk_error_trap_pop ();
+ if (err != Success || result != Success)
+ return FALSE;
+
+ if (type != XA_WINDOW)
+ {
+ XFree (w);
+ return FALSE;
+ }
+
+ *val = *w;
+ XFree (w);
+
+ return TRUE;
+}
+
+static void
+update_event_windows (WnckWindow *win)
+{
+ Display *xdisplay;
+ decor_t *d = g_object_get_data (G_OBJECT (win), "decor");
+ gint x0, y0, width, height, x, y, w, h;
+ gint i, j;
+
+ xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
+
+ wnck_window_get_geometry (win, &x0, &y0, &width, &height);
+
+ gdk_error_trap_push ();
+
+ for (i = 0; i < 3; i++)
+ {
+ static guint event_window_actions[3][3] = {
+ {
+ WNCK_WINDOW_ACTION_RESIZE,
+ WNCK_WINDOW_ACTION_RESIZE,
+ WNCK_WINDOW_ACTION_RESIZE
+ }, {
+ WNCK_WINDOW_ACTION_RESIZE,
+ WNCK_WINDOW_ACTION_MOVE,
+ WNCK_WINDOW_ACTION_RESIZE
+ }, {
+ WNCK_WINDOW_ACTION_RESIZE,
+ WNCK_WINDOW_ACTION_RESIZE,
+ WNCK_WINDOW_ACTION_RESIZE
+ }
+ };
+
+ for (j = 0; j < 3; j++)
+ {
+ if (d->actions & event_window_actions[i][j])
+ {
+ x = pos[i][j].x + pos[i][j].xw * width;
+ y = pos[i][j].y + pos[i][j].yh * height;
+ w = pos[i][j].w + pos[i][j].ww * width;
+ h = pos[i][j].h + pos[i][j].hh * height;
+
+ XMapWindow (xdisplay, d->event_windows[i][j]);
+ XMoveResizeWindow (xdisplay, d->event_windows[i][j],
+ x, y, w, h);
+ }
+ else
+ {
+ XUnmapWindow (xdisplay, d->event_windows[i][j]);
+ }
+ }
+ }
+
+ for (i = 0; i < 3; i++)
+ {
+ static guint button_actions[3] = {
+ WNCK_WINDOW_ACTION_CLOSE,
+ WNCK_WINDOW_ACTION_MAXIMIZE,
+ WNCK_WINDOW_ACTION_MINIMIZE
+ };
+
+ if (d->actions & button_actions[i])
+ {
+ x = bpos[i].x + bpos[i].xw * width;
+ y = bpos[i].y + bpos[i].yh * height;
+ w = bpos[i].w + bpos[i].ww * width;
+ h = bpos[i].h + bpos[i].hh * height;
+
+ XMapWindow (xdisplay, d->button_windows[i]);
+ XMoveResizeWindow (xdisplay, d->button_windows[i], x, y, w, h);
+ }
+ else
+ XUnmapWindow (xdisplay, d->button_windows[i]);
+ }
+
+ XSync (xdisplay, FALSE);
+ gdk_error_trap_pop ();
+}
+
+static gint
+max_window_name_width (WnckWindow *win)
+{
+ decor_t *d = g_object_get_data (G_OBJECT (win), "decor");
+ const gchar *name;
+ gint w;
+
+ name = wnck_window_get_name (win);
+ if (!name)
+ return 0;
+
+ if (!d->layout)
+ {
+ d->layout = pango_layout_new (pango_context);
+ if (!d->layout)
+ return 0;
+
+ pango_layout_set_wrap (d->layout, PANGO_WRAP_CHAR);
+ }
+
+ pango_layout_set_width (d->layout, -1);
+ pango_layout_set_text (d->layout, name, strlen (name));
+ pango_layout_get_pixel_size (d->layout, &w, NULL);
+
+ if (d->name)
+ pango_layout_set_text (d->layout, d->name, strlen (d->name));
+
+ return w + 4;
+}
+
+static void
+update_window_decoration_name (WnckWindow *win)
+{
+ decor_t *d = g_object_get_data (G_OBJECT (win), "decor");
+ const gchar *name;
+ glong name_length;
+ PangoLayoutLine *line;
+
+ if (d->name)
+ {
+ g_free (d->name);
+ d->name = NULL;
+ }
+
+ name = wnck_window_get_name (win);
+ if (name && (name_length = strlen (name)))
+ {
+ gint w, n_line;
+
+ w = d->width - LEFT_SPACE - RIGHT_SPACE - BUTTON_SPACE - ICON_SPACE - 4;
+ if (w < 1)
+ w = 1;
+
+ pango_layout_set_width (d->layout, w * PANGO_SCALE);
+ pango_layout_set_text (d->layout, name, name_length);
+
+ n_line = pango_layout_get_line_count (d->layout);
+
+ line = pango_layout_get_line (d->layout, 0);
+
+ name_length = line->length;
+ if (pango_layout_get_line_count (d->layout) > 1)
+ {
+ if (name_length < 4)
+ {
+ g_object_unref (G_OBJECT (d->layout));
+ d->layout = NULL;
+ return;
+ }
+
+ d->name = g_strndup (name, name_length);
+ strcpy (d->name + name_length - 3, "...");
+ }
+ else
+ d->name = g_strndup (name, name_length);
+
+ pango_layout_set_text (d->layout, d->name, name_length);
+ }
+ else if (d->layout)
+ {
+ g_object_unref (G_OBJECT (d->layout));
+ d->layout = NULL;
+ }
+}
+
+static void
+update_window_decoration_icon (WnckWindow *win)
+{
+ decor_t *d = g_object_get_data (G_OBJECT (win), "decor");
+ GdkPixbuf *icon;
+
+ if (d->icon)
+ {
+ cairo_pattern_destroy (d->icon);
+ d->icon = NULL;
+ }
+
+ if (d->icon_pixmap)
+ {
+ gdk_pixmap_unref (d->icon_pixmap);
+ d->icon_pixmap = NULL;
+ }
+
+ icon = wnck_window_get_mini_icon (win);
+ if (icon)
+ {
+ cairo_t *cr;
+
+ d->icon_pixmap = pixmap_new_from_pixbuf (icon);
+ cr = gdk_cairo_create (GDK_DRAWABLE (d->icon_pixmap));
+ d->icon = cairo_pattern_create_for_surface (cairo_get_target (cr));
+ cairo_destroy (cr);
+ }
+}
+
+static void
+update_window_decoration_state (WnckWindow *win)
+{
+ decor_t *d = g_object_get_data (G_OBJECT (win), "decor");
+
+ d->state = wnck_window_get_state (win);
+}
+
+static void
+update_window_decoration_actions (WnckWindow *win)
+{
+ decor_t *d = g_object_get_data (G_OBJECT (win), "decor");
+
+ d->actions = wnck_window_get_actions (win);
+}
+
+static gboolean
+update_window_decoration_size (WnckWindow *win)
+{
+ decor_t *d = g_object_get_data (G_OBJECT (win), "decor");
+ GdkPixmap *pixmap, *buffer_pixmap = NULL;
+ gint width, height;
+ gint w;
+
+ width = max_window_name_width (win) + BUTTON_SPACE + ICON_SPACE;
+
+ wnck_window_get_geometry (win, NULL, NULL, &w, NULL);
+ if (w < width)
+ width = MAX (ICON_SPACE + BUTTON_SPACE, w);
+
+ width += LEFT_SPACE + RIGHT_SPACE;
+ height = TOP_SPACE + 1 + BOTTOM_SPACE;
+
+ if (width == d->width && height == d->height)
+ return FALSE;
+
+ pixmap = create_pixmap (width, height);
+ if (!pixmap)
+ return FALSE;
+
+ if (double_buffering)
+ {
+ buffer_pixmap = create_pixmap (width, height);
+ if (!buffer_pixmap)
+ {
+ gdk_pixmap_unref (pixmap);
+ return FALSE;
+ }
+ }
+
+ if (d->pixmap)
+ gdk_pixmap_unref (d->pixmap);
+
+ if (d->buffer_pixmap)
+ gdk_pixmap_unref (d->buffer_pixmap);
+
+ if (d->gc)
+ gdk_gc_unref (d->gc);
+
+ d->pixmap = pixmap;
+ d->buffer_pixmap = buffer_pixmap;
+ d->gc = gdk_gc_new (pixmap);
+
+ d->width = width;
+ d->height = height;
+
+ d->prop_xid = wnck_window_get_xid (win);
+
+ update_window_decoration_name (win);
+
+ queue_decor_draw (d);
+
+ return TRUE;
+}
+
+static void
+add_frame_window (WnckWindow *win,
+ Window frame)
+{
+ Display *xdisplay;
+ XSetWindowAttributes attr;
+ gulong xid = wnck_window_get_xid (win);
+ decor_t *d = g_object_get_data (G_OBJECT (win), "decor");
+ gint i, j;
+
+ xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
+
+ attr.event_mask = ButtonPressMask | EnterWindowMask | LeaveWindowMask;
+ attr.override_redirect = TRUE;
+
+ gdk_error_trap_push ();
+
+ for (i = 0; i < 3; i++)
+ {
+ for (j = 0; j < 3; j++)
+ {
+ d->event_windows[i][j] =
+ XCreateWindow (xdisplay,
+ frame,
+ 0, 0, 1, 1, 0,
+ CopyFromParent, CopyFromParent, CopyFromParent,
+ CWOverrideRedirect | CWEventMask, &attr);
+
+ if (cursor[i][j].cursor)
+ XDefineCursor (xdisplay, d->event_windows[i][j],
+ cursor[i][j].cursor);
+ }
+ }
+
+ attr.event_mask |= ButtonReleaseMask;
+
+ for (i = 0; i < 3; i++)
+ {
+ d->button_windows[i] =
+ XCreateWindow (xdisplay,
+ frame,
+ 0, 0, 1, 1, 0,
+ CopyFromParent, CopyFromParent, CopyFromParent,
+ CWOverrideRedirect | CWEventMask, &attr);
+
+ d->button_states[i] = 0;
+ }
+
+ XSync (xdisplay, FALSE);
+ if (!gdk_error_trap_pop ())
+ {
+ d->decorated = TRUE;
+
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 3; j++)
+ g_hash_table_insert (frame_table,
+ GINT_TO_POINTER (d->event_windows[i][j]),
+ GINT_TO_POINTER (xid));
+
+ for (i = 0; i < 3; i++)
+ g_hash_table_insert (frame_table,
+ GINT_TO_POINTER (d->button_windows[i]),
+ GINT_TO_POINTER (xid));
+
+
+ update_window_decoration_state (win);
+ update_window_decoration_actions (win);
+ update_window_decoration_icon (win);
+ update_window_decoration_size (win);
+
+ update_event_windows (win);
+ }
+ else
+ {
+ memset (d->event_windows, 0, sizeof (d->event_windows));
+ }
+}
+
+static void
+remove_frame_window (WnckWindow *win)
+{
+ decor_t *d = g_object_get_data (G_OBJECT (win), "decor");
+
+ if (d->pixmap)
+ {
+ gdk_pixmap_unref (d->pixmap);
+ d->pixmap = NULL;
+ }
+
+ if (d->buffer_pixmap)
+ {
+ gdk_pixmap_unref (d->buffer_pixmap);
+ d->buffer_pixmap = NULL;
+ }
+
+ if (d->gc)
+ {
+ gdk_gc_unref (d->gc);
+ d->gc = NULL;
+ }
+
+ if (d->name)
+ {
+ g_free (d->name);
+ d->name = NULL;
+ }
+
+ if (d->layout)
+ {
+ g_object_unref (G_OBJECT (d->layout));
+ d->layout = NULL;
+ }
+
+ if (d->icon)
+ {
+ cairo_pattern_destroy (d->icon);
+ d->icon = NULL;
+ }
+
+ if (d->icon_pixmap)
+ {
+ gdk_pixmap_unref (d->icon_pixmap);
+ d->icon_pixmap = NULL;
+ }
+
+ d->width = 0;
+ d->height = 0;
+
+ d->decorated = FALSE;
+
+ d->state = 0;
+ d->actions = 0;
+
+ draw_list = g_slist_remove (draw_list, d);
+}
+
+static void
+window_name_changed (WnckWindow *win)
+{
+ decor_t *d = g_object_get_data (G_OBJECT (win), "decor");
+
+ if (d->decorated)
+ {
+ if (update_window_decoration_size (win))
+ queue_decor_draw (d);
+ }
+}
+
+static void
+window_geometry_changed (WnckWindow *win)
+{
+ decor_t *d = g_object_get_data (G_OBJECT (win), "decor");
+
+ if (d->decorated)
+ {
+ update_window_decoration_size (win);
+ update_event_windows (win);
+ }
+}
+
+static void
+window_icon_changed (WnckWindow *win)
+{
+ decor_t *d = g_object_get_data (G_OBJECT (win), "decor");
+
+ if (d->decorated)
+ {
+ update_window_decoration_icon (win);
+ queue_decor_draw (d);
+ }
+}
+
+static void
+window_state_changed (WnckWindow *win)
+{
+ decor_t *d = g_object_get_data (G_OBJECT (win), "decor");
+
+ if (d->decorated)
+ {
+ update_window_decoration_state (win);
+ queue_decor_draw (d);
+ }
+}
+
+static void
+window_actions_changed (WnckWindow *win)
+{
+ decor_t *d = g_object_get_data (G_OBJECT (win), "decor");
+
+ if (d->decorated)
+ {
+ update_window_decoration_actions (win);
+ queue_decor_draw (d);
+ }
+}
+
+static void
+connect_window (WnckWindow *win)
+{
+ g_signal_connect_object (win, "name_changed",
+ G_CALLBACK (window_name_changed),
+ 0, 0);
+ g_signal_connect_object (win, "geometry_changed",
+ G_CALLBACK (window_geometry_changed),
+ 0, 0);
+ g_signal_connect_object (win, "icon_changed",
+ G_CALLBACK (window_icon_changed),
+ 0, 0);
+ g_signal_connect_object (win, "state_changed",
+ G_CALLBACK (window_state_changed),
+ 0, 0);
+ g_signal_connect_object (win, "actions_changed",
+ G_CALLBACK (window_actions_changed),
+ 0, 0);
+}
+
+static void
+active_window_changed (WnckScreen *screen)
+{
+ WnckWindow *win;
+ decor_t *d;
+
+ win = wnck_screen_get_previously_active_window (screen);
+ if (win)
+ {
+ d = g_object_get_data (G_OBJECT (win), "decor");
+ if (d->pixmap)
+ {
+ d->active = wnck_window_is_active (win);
+ queue_decor_draw (d);
+ }
+ }
+
+ win = wnck_screen_get_active_window (screen);
+ if (win)
+ {
+ d = g_object_get_data (G_OBJECT (win), "decor");
+ if (d->pixmap)
+ {
+ d->active = wnck_window_is_active (win);
+ queue_decor_draw (d);
+ }
+ }
+}
+
+static void
+window_opened (WnckScreen *screen,
+ WnckWindow *win)
+{
+ decor_t *d;
+ Window frame;
+ gulong xid;
+
+ d = g_malloc (sizeof (decor_t));
+ if (!d)
+ return;
+
+ d->pixmap = NULL;
+ d->buffer_pixmap = NULL;
+ d->gc = NULL;
+
+ d->icon = NULL;
+ d->icon_pixmap = NULL;
+
+ d->width = 0;
+ d->height = 0;
+
+ d->active = wnck_window_is_active (win);
+
+ d->layout = NULL;
+ d->name = NULL;
+
+ d->state = 0;
+ d->actions = 0;
+
+ d->prop_xid = 0;
+
+ d->decorated = FALSE;
+
+ g_object_set_data (G_OBJECT (win), "decor", d);
+
+ connect_window (win);
+
+ xid = wnck_window_get_xid (win);
+
+ if (get_window_prop (xid, frame_window_atom, &frame))
+ add_frame_window (win, frame);
+}
+
+static void
+window_closed (WnckScreen *screen,
+ WnckWindow *win)
+{
+ decor_t *d = g_object_get_data (G_OBJECT (win), "decor");
+
+ remove_frame_window (win);
+
+ g_free (d);
+}
+
+static void
+connect_screen (WnckScreen *screen)
+{
+ GList *windows;
+
+ g_signal_connect_object (G_OBJECT (screen), "active_window_changed",
+ G_CALLBACK (active_window_changed),
+ 0, 0);
+ g_signal_connect_object (G_OBJECT (screen), "window_opened",
+ G_CALLBACK (window_opened),
+ 0, 0);
+ g_signal_connect_object (G_OBJECT (screen), "window_closed",
+ G_CALLBACK (window_closed),
+ 0, 0);
+
+ windows = wnck_screen_get_windows (screen);
+ while (windows != NULL)
+ {
+ window_opened (screen, windows->data);
+ windows = windows->next;
+ }
+}
+
+static void
+move_resize_window (WnckWindow *win,
+ int direction,
+ XEvent *xevent)
+{
+ Display *xdisplay;
+ GdkDisplay *gdkdisplay;
+ GdkScreen *screen;
+ Window xroot;
+ XEvent ev;
+
+ gdkdisplay = gdk_display_get_default ();
+ xdisplay = GDK_DISPLAY_XDISPLAY (gdkdisplay);
+ screen = gdk_display_get_default_screen (gdkdisplay);
+ xroot = RootWindowOfScreen (gdk_x11_screen_get_xscreen (screen));
+
+ if (action_menu_mapped)
+ {
+ gtk_object_destroy (GTK_OBJECT (action_menu));
+ action_menu_mapped = FALSE;
+ action_menu = NULL;
+ return;
+ }
+
+ ev.xclient.type = ClientMessage;
+ ev.xclient.display = xdisplay;
+
+ ev.xclient.serial = 0;
+ ev.xclient.send_event = TRUE;
+
+ ev.xclient.window = wnck_window_get_xid (win);
+ ev.xclient.message_type = wm_move_resize_atom;
+ ev.xclient.format = 32;
+
+ ev.xclient.data.l[0] = xevent->xbutton.x_root;
+ ev.xclient.data.l[1] = xevent->xbutton.y_root;
+ ev.xclient.data.l[2] = direction;
+ ev.xclient.data.l[3] = xevent->xbutton.button;
+ ev.xclient.data.l[4] = 1;
+
+ XUngrabPointer (xdisplay, xevent->xbutton.time);
+ XUngrabKeyboard (xdisplay, xevent->xbutton.time);
+
+ XSendEvent (xdisplay, xroot, FALSE,
+ SubstructureRedirectMask | SubstructureNotifyMask,
+ &ev);
+
+ XSync (xdisplay, FALSE);
+}
+
+/* stolen from gtktooltip.c */
+
+#define DEFAULT_DELAY 500 /* Default delay in ms */
+#define STICKY_DELAY 0 /* Delay before popping up next tip
+ * if we're sticky
+ */
+#define STICKY_REVERT_DELAY 1000 /* Delay before sticky tooltips revert
+ * to normal
+ */
+
+static void
+show_tooltip (const char *text)
+{
+ GdkDisplay *gdkdisplay;
+ GtkRequisition requisition;
+ gint x, y, w, h;
+ GdkScreen *screen;
+ gint monitor_num;
+ GdkRectangle monitor;
+
+ gdkdisplay = gdk_display_get_default ();
+
+ gtk_label_set_text (GTK_LABEL (tip_label), text);
+
+ gtk_widget_size_request (tip_window, &requisition);
+
+ w = requisition.width;
+ h = requisition.height;
+
+ gdk_display_get_pointer (gdkdisplay, &screen, &x, &y, NULL);
+
+ x -= (w / 2 + 4);
+
+ monitor_num = gdk_screen_get_monitor_at_point (screen, x, y);
+ gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
+
+ if ((x + w) > monitor.x + monitor.width)
+ x -= (x + w) - (monitor.x + monitor.width);
+ else if (x < monitor.x)
+ x = monitor.x;
+
+ if ((y + h + 16) > monitor.y + monitor.height)
+ y = y - h - 16;
+ else
+ y = y + 16;
+
+ gtk_window_move (GTK_WINDOW (tip_window), x, y);
+ gtk_widget_show (tip_window);
+}
+
+static void
+hide_tooltip (void)
+{
+ if (GTK_WIDGET_VISIBLE (tip_window))
+ g_get_current_time (&tooltip_last_popdown);
+
+ gtk_widget_hide (tip_window);
+
+ if (tooltip_timer_tag)
+ {
+ g_source_remove (tooltip_timer_tag);
+ tooltip_timer_tag = 0;
+ }
+}
+
+static gboolean
+tooltip_recently_shown (void)
+{
+ GTimeVal now;
+ glong msec;
+
+ g_get_current_time (&now);
+
+ msec = (now.tv_sec - tooltip_last_popdown.tv_sec) * 1000 +
+ (now.tv_usec - tooltip_last_popdown.tv_usec) / 1000;
+
+ return (msec < STICKY_REVERT_DELAY);
+}
+
+static gint
+tooltip_timeout (gpointer data)
+{
+ tooltip_timer_tag = 0;
+
+ show_tooltip ((const char *) data);
+
+ return FALSE;
+}
+
+static void
+tooltip_start_delay (const char *text)
+{
+ guint delay = DEFAULT_DELAY;
+
+ if (tooltip_timer_tag)
+ return;
+
+ if (tooltip_recently_shown ())
+ delay = STICKY_DELAY;
+
+ tooltip_timer_tag = g_timeout_add (delay,
+ tooltip_timeout,
+ (gpointer) text);
+}
+
+static gint
+tooltip_paint_window (GtkWidget *tooltip)
+{
+ GtkRequisition req;
+
+ gtk_widget_size_request (tip_window, &req);
+ gtk_paint_flat_box (tip_window->style, tip_window->window,
+ GTK_STATE_NORMAL, GTK_SHADOW_OUT,
+ NULL, GTK_WIDGET (tip_window), "tooltip",
+ 0, 0, req.width, req.height);
+
+ return FALSE;
+}
+
+static gboolean
+create_tooltip_window (void)
+{
+ tip_window = gtk_window_new (GTK_WINDOW_POPUP);
+
+ gtk_widget_set_app_paintable (tip_window, TRUE);
+ gtk_window_set_resizable (GTK_WINDOW (tip_window), FALSE);
+ gtk_widget_set_name (tip_window, "gtk-tooltips");
+ gtk_container_set_border_width (GTK_CONTAINER (tip_window), 4);
+
+ g_signal_connect_swapped (tip_window,
+ "expose_event",
+ G_CALLBACK (tooltip_paint_window),
+ 0);
+
+ tip_label = gtk_label_new (NULL);
+ gtk_label_set_line_wrap (GTK_LABEL (tip_label), TRUE);
+ gtk_misc_set_alignment (GTK_MISC (tip_label), 0.5, 0.5);
+ gtk_widget_show (tip_label);
+
+ gtk_container_add (GTK_CONTAINER (tip_window), tip_label);
+
+ gtk_widget_ensure_style (tip_window);
+
+ return TRUE;
+}
+
+static void
+handle_tooltip_event (WnckWindow *win,
+ XEvent *xevent,
+ guint state,
+ const char *tip)
+{
+ switch (xevent->type) {
+ case ButtonPress:
+ hide_tooltip ();
+ break;
+ case ButtonRelease:
+ break;
+ case EnterNotify:
+ if (!(state & PRESSED_EVENT_WINDOW))
+ {
+ if (wnck_window_is_active (win))
+ tooltip_start_delay (tip);
+ }
+ break;
+ case LeaveNotify:
+ if (xevent->xcrossing.mode != NotifyGrab)
+ hide_tooltip ();
+ break;
+ }
+}
+
+static void
+close_button_event (WnckWindow *win,
+ XEvent *xevent)
+{
+ decor_t *d = g_object_get_data (G_OBJECT (win), "decor");
+ guint state = d->button_states[0];
+
+ handle_tooltip_event (win, xevent, state, "Close Window");
+
+ switch (xevent->type) {
+ case ButtonPress:
+ d->button_states[0] |= PRESSED_EVENT_WINDOW;
+ break;
+ case ButtonRelease:
+ if (d->button_states[0] == (PRESSED_EVENT_WINDOW | IN_EVENT_WINDOW))
+ wnck_window_close (win, xevent->xbutton.time);
+
+ d->button_states[0] &= ~PRESSED_EVENT_WINDOW;
+ break;
+ case EnterNotify:
+ d->button_states[0] |= IN_EVENT_WINDOW;
+ break;
+ case LeaveNotify:
+ if (xevent->xcrossing.mode != NotifyGrab)
+ d->button_states[0] &= ~IN_EVENT_WINDOW;
+ break;
+ }
+
+ if (state != d->button_states[0])
+ queue_decor_draw (d);
+}
+
+static void
+max_button_event (WnckWindow *win,
+ XEvent *xevent)
+{
+ decor_t *d = g_object_get_data (G_OBJECT (win), "decor");
+ guint state = d->button_states[1];
+
+ if (wnck_window_is_maximized (win))
+ handle_tooltip_event (win, xevent, state, "Unmaximize Window");
+ else
+ handle_tooltip_event (win, xevent, state, "Maximize Window");
+
+ switch (xevent->type) {
+ case ButtonPress:
+ d->button_states[1] |= PRESSED_EVENT_WINDOW;
+ break;
+ case ButtonRelease:
+ if (d->button_states[1] == (PRESSED_EVENT_WINDOW | IN_EVENT_WINDOW))
+ {
+ if (wnck_window_is_maximized (win))
+ wnck_window_unmaximize (win);
+ else
+ wnck_window_maximize (win);
+ }
+
+ d->button_states[1] &= ~PRESSED_EVENT_WINDOW;
+ break;
+ case EnterNotify:
+ d->button_states[1] |= IN_EVENT_WINDOW;
+ break;
+ case LeaveNotify:
+ if (xevent->xcrossing.mode != NotifyGrab)
+ d->button_states[1] &= ~IN_EVENT_WINDOW;
+ break;
+ }
+
+ if (state != d->button_states[1])
+ queue_decor_draw (d);
+}
+
+static void
+min_button_event (WnckWindow *win,
+ XEvent *xevent)
+{
+ decor_t *d = g_object_get_data (G_OBJECT (win), "decor");
+ guint state = d->button_states[2];
+
+ handle_tooltip_event (win, xevent, state, "Minimize Window");
+
+ switch (xevent->type) {
+ case ButtonPress:
+ d->button_states[2] |= PRESSED_EVENT_WINDOW;
+ break;
+ case ButtonRelease:
+ if (d->button_states[2] == (PRESSED_EVENT_WINDOW | IN_EVENT_WINDOW))
+ wnck_window_minimize (win);
+
+ d->button_states[2] &= ~PRESSED_EVENT_WINDOW;
+ break;
+ case EnterNotify:
+ d->button_states[2] |= IN_EVENT_WINDOW;
+ if (wnck_window_is_active (win))
+ tooltip_start_delay ("Minimize Window");
+ break;
+ case LeaveNotify:
+ if (xevent->xcrossing.mode != NotifyGrab)
+ d->button_states[2] &= ~IN_EVENT_WINDOW;
+ break;
+ }
+
+ if (state != d->button_states[2])
+ queue_decor_draw (d);
+}
+
+static void
+top_left_event (WnckWindow *win,
+ XEvent *xevent)
+{
+ if (xevent->xbutton.button == 1)
+ move_resize_window (win, WM_MOVERESIZE_SIZE_TOPLEFT, xevent);
+}
+
+static void
+top_event (WnckWindow *win,
+ XEvent *xevent)
+{
+ if (xevent->xbutton.button == 1)
+ move_resize_window (win, WM_MOVERESIZE_SIZE_TOP, xevent);
+}
+
+static void
+top_right_event (WnckWindow *win,
+ XEvent *xevent)
+{
+ if (xevent->xbutton.button == 1)
+ move_resize_window (win, WM_MOVERESIZE_SIZE_TOPRIGHT, xevent);
+}
+
+static void
+left_event (WnckWindow *win,
+ XEvent *xevent)
+{
+ if (xevent->xbutton.button == 1)
+ move_resize_window (win, WM_MOVERESIZE_SIZE_LEFT, xevent);
+}
+
+static void
+action_menu_unmap (GObject *object)
+{
+ action_menu_mapped = FALSE;
+}
+
+static void
+title_event (WnckWindow *win,
+ XEvent *xevent)
+{
+ static int last_button_num = 0;
+ static Window last_button_xwindow = None;
+ static Time last_button_time = 0;
+
+ if (xevent->type != ButtonPress)
+ return;
+
+ if (xevent->xbutton.button == 1)
+ {
+ if (xevent->xbutton.button == last_button_num &&
+ xevent->xbutton.window == last_button_xwindow &&
+ xevent->xbutton.time < last_button_time + double_click_timeout)
+ {
+ if (wnck_window_is_maximized (win))
+ wnck_window_unmaximize (win);
+ else
+ wnck_window_maximize (win);
+
+ last_button_num = 0;
+ last_button_xwindow = None;
+ last_button_time = 0;
+ }
+ else
+ {
+ last_button_num = xevent->xbutton.button;
+ last_button_xwindow = xevent->xbutton.window;
+ last_button_time = xevent->xbutton.time;
+
+ move_resize_window (win, WM_MOVERESIZE_MOVE, xevent);
+ }
+ }
+ else if (xevent->xbutton.button == 3)
+ {
+ GdkDisplay *gdkdisplay;
+ GdkScreen *screen;
+
+ gdkdisplay = gdk_display_get_default ();
+ screen = gdk_display_get_default_screen (gdkdisplay);
+
+ if (action_menu)
+ gtk_object_destroy (GTK_OBJECT (action_menu));
+
+ action_menu = wnck_create_window_action_menu (win);
+
+ gtk_menu_set_screen (GTK_MENU (action_menu), screen);
+
+ g_signal_connect_object (G_OBJECT (action_menu), "unmap",
+ G_CALLBACK (action_menu_unmap),
+ 0, 0);
+
+ gtk_widget_show (action_menu);
+ gtk_menu_popup (GTK_MENU (action_menu),
+ NULL, NULL,
+ NULL, NULL,
+ xevent->xbutton.button,
+ xevent->xbutton.time);
+
+ action_menu_mapped = TRUE;
+ }
+}
+
+static void
+right_event (WnckWindow *win,
+ XEvent *xevent)
+{
+ if (xevent->xbutton.button == 1)
+ move_resize_window (win, WM_MOVERESIZE_SIZE_RIGHT, xevent);
+}
+
+static void
+bottom_left_event (WnckWindow *win,
+ XEvent *xevent)
+{
+ if (xevent->xbutton.button == 1)
+ move_resize_window (win, WM_MOVERESIZE_SIZE_BOTTOMLEFT, xevent);
+}
+
+static void
+bottom_event (WnckWindow *win,
+ XEvent *xevent)
+{
+ if (xevent->xbutton.button == 1)
+ move_resize_window (win, WM_MOVERESIZE_SIZE_BOTTOM, xevent);
+}
+
+static void
+bottom_right_event (WnckWindow *win,
+ XEvent *xevent)
+{
+ if (xevent->xbutton.button == 1)
+ move_resize_window (win, WM_MOVERESIZE_SIZE_BOTTOMRIGHT, xevent);
+}
+
+static GdkFilterReturn
+event_filter_func (GdkXEvent *gdkxevent,
+ GdkEvent *event,
+ gpointer data)
+{
+ Display *xdisplay;
+ GdkDisplay *gdkdisplay;
+ XEvent *xevent = gdkxevent;
+ gulong xid = 0;
+
+ gdkdisplay = gdk_display_get_default ();
+ xdisplay = GDK_DISPLAY_XDISPLAY (gdkdisplay);
+
+ switch (xevent->type) {
+ case ButtonPress:
+ case ButtonRelease:
+ xid = (gulong)
+ g_hash_table_lookup (frame_table,
+ GINT_TO_POINTER (xevent->xbutton.window));
+ break;
+ case EnterNotify:
+ case LeaveNotify:
+ xid = (gulong)
+ g_hash_table_lookup (frame_table,
+ GINT_TO_POINTER (xevent->xcrossing.window));
+ break;
+ case MotionNotify:
+ xid = (gulong)
+ g_hash_table_lookup (frame_table,
+ GINT_TO_POINTER (xevent->xmotion.window));
+ break;
+ case PropertyNotify:
+ if (xevent->xproperty.atom == frame_window_atom)
+ {
+ WnckWindow *win;
+
+ xid = xevent->xproperty.window;
+
+ win = wnck_window_get (xid);
+ if (win)
+ {
+ Window frame;
+
+ if (get_window_prop (xid, frame_window_atom, &frame))
+ add_frame_window (win, frame);
+ else
+ remove_frame_window (win);
+ }
+ }
+ break;
+ case DestroyNotify:
+ g_hash_table_remove (frame_table,
+ GINT_TO_POINTER (xevent->xproperty.window));
+ default:
+ break;
+ }
+
+ if (xid)
+ {
+ WnckWindow *win;
+
+ win = wnck_window_get (xid);
+ if (win)
+ {
+ static event_callback callback[3][3] = {
+ { top_left_event, top_event, top_right_event },
+ { left_event, title_event, right_event },
+ { bottom_left_event, bottom_event, bottom_right_event }
+ };
+ static event_callback button_callback[3] = {
+ close_button_event,
+ max_button_event,
+ min_button_event
+ };
+ decor_t *d = g_object_get_data (G_OBJECT (win), "decor");
+
+ if (d->decorated)
+ {
+ gint i, j;
+
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 3; j++)
+ if (d->event_windows[i][j] == xevent->xany.window)
+ (*callback[i][j]) (win, xevent);
+
+ for (i = 0; i < 3; i++)
+ if (d->button_windows[i] == xevent->xany.window)
+ (*button_callback[i]) (win, xevent);
+ }
+ }
+ }
+
+ return GDK_FILTER_CONTINUE;
+}
+
+/* from clearlooks theme */
+static void
+rgb_to_hls (gdouble *r,
+ gdouble *g,
+ gdouble *b)
+{
+ gdouble min;
+ gdouble max;
+ gdouble red;
+ gdouble green;
+ gdouble blue;
+ gdouble h, l, s;
+ gdouble delta;
+
+ red = *r;
+ green = *g;
+ blue = *b;
+
+ if (red > green)
+ {
+ if (red > blue)
+ max = red;
+ else
+ max = blue;
+
+ if (green < blue)
+ min = green;
+ else
+ min = blue;
+ }
+ else
+ {
+ if (green > blue)
+ max = green;
+ else
+ max = blue;
+
+ if (red < blue)
+ min = red;
+ else
+ min = blue;
+ }
+
+ l = (max + min) / 2;
+ s = 0;
+ h = 0;
+
+ if (max != min)
+ {
+ if (l <= 0.5)
+ s = (max - min) / (max + min);
+ else
+ s = (max - min) / (2 - max - min);
+
+ delta = max -min;
+ if (red == max)
+ h = (green - blue) / delta;
+ else if (green == max)
+ h = 2 + (blue - red) / delta;
+ else if (blue == max)
+ h = 4 + (red - green) / delta;
+
+ h *= 60;
+ if (h < 0.0)
+ h += 360;
+ }
+
+ *r = h;
+ *g = l;
+ *b = s;
+}
+
+static void
+hls_to_rgb (gdouble *h,
+ gdouble *l,
+ gdouble *s)
+{
+ gdouble hue;
+ gdouble lightness;
+ gdouble saturation;
+ gdouble m1, m2;
+ gdouble r, g, b;
+
+ lightness = *l;
+ saturation = *s;
+
+ if (lightness <= 0.5)
+ m2 = lightness * (1 + saturation);
+ else
+ m2 = lightness + saturation - lightness * saturation;
+
+ m1 = 2 * lightness - m2;
+
+ if (saturation == 0)
+ {
+ *h = lightness;
+ *l = lightness;
+ *s = lightness;
+ }
+ else
+ {
+ hue = *h + 120;
+ while (hue > 360)
+ hue -= 360;
+ while (hue < 0)
+ hue += 360;
+
+ if (hue < 60)
+ r = m1 + (m2 - m1) * hue / 60;
+ else if (hue < 180)
+ r = m2;
+ else if (hue < 240)
+ r = m1 + (m2 - m1) * (240 - hue) / 60;
+ else
+ r = m1;
+
+ hue = *h;
+ while (hue > 360)
+ hue -= 360;
+ while (hue < 0)
+ hue += 360;
+
+ if (hue < 60)
+ g = m1 + (m2 - m1) * hue / 60;
+ else if (hue < 180)
+ g = m2;
+ else if (hue < 240)
+ g = m1 + (m2 - m1) * (240 - hue) / 60;
+ else
+ g = m1;
+
+ hue = *h - 120;
+ while (hue > 360)
+ hue -= 360;
+ while (hue < 0)
+ hue += 360;
+
+ if (hue < 60)
+ b = m1 + (m2 - m1) * hue / 60;
+ else if (hue < 180)
+ b = m2;
+ else if (hue < 240)
+ b = m1 + (m2 - m1) * (240 - hue) / 60;
+ else
+ b = m1;
+
+ *h = r;
+ *l = g;
+ *s = b;
+ }
+}
+
+static void
+shade (const decor_color_t *a,
+ decor_color_t *b,
+ float k)
+{
+ double red;
+ double green;
+ double blue;
+
+ red = a->r;
+ green = a->g;
+ blue = a->b;
+
+ rgb_to_hls (&red, &green, &blue);
+
+ green *= k;
+ if (green > 1.0)
+ green = 1.0;
+ else if (green < 0.0)
+ green = 0.0;
+
+ blue *= k;
+ if (blue > 1.0)
+ blue = 1.0;
+ else if (blue < 0.0)
+ blue = 0.0;
+
+ hls_to_rgb (&red, &green, &blue);
+
+ b->r = red;
+ b->g = green;
+ b->b = blue;
+}
+
+static void
+style_changed (GtkWidget *widget)
+{
+ GdkDisplay *gdkdisplay;
+ GdkScreen *gdkscreen;
+ GtkStyle *style;
+ decor_color_t spot_color;
+ WnckScreen *screen;
+ GList *windows;
+
+ gdkdisplay = gdk_display_get_default ();
+ gdkscreen = gdk_display_get_default_screen (gdkdisplay);
+ screen = wnck_screen_get_default ();
+
+ style = gtk_widget_get_style (widget);
+
+ spot_color.r = style->bg[GTK_STATE_SELECTED].red / 65535.0;
+ spot_color.g = style->bg[GTK_STATE_SELECTED].green / 65535.0;
+ spot_color.b = style->bg[GTK_STATE_SELECTED].blue / 65535.0;
+
+ shade (&spot_color, &_title_color[0], 1.05);
+ shade (&_title_color[0], &_title_color[1], 0.85);
+
+ update_default_decorations (gdkscreen);
+
+ windows = wnck_screen_get_windows (screen);
+ while (windows != NULL)
+ {
+ queue_decor_draw (g_object_get_data (G_OBJECT (windows->data),
+ "decor"));
+ windows = windows->next;
+ }
+}
+
+static gboolean
+init_settings (WnckScreen *screen)
+{
+ PangoFontDescription *desc;
+ GtkSettings *settings;
+
+ style_window = gtk_window_new (GTK_WINDOW_POPUP);
+ gtk_widget_ensure_style (style_window);
+
+ style_changed (style_window);
+
+ g_signal_connect_object (style_window, "style-set",
+ G_CALLBACK (style_changed),
+ 0, 0);
+
+ settings = gtk_widget_get_settings (style_window);
+
+ g_object_get (G_OBJECT (settings), "gtk-double-click-time",
+ &double_click_timeout, NULL);
+
+ pango_context = gtk_widget_create_pango_context (style_window);
+
+ desc = pango_font_description_from_string ("Sans Bold 10");
+
+ pango_context_set_font_description (pango_context, desc);
+
+ return TRUE;
+}
+
+int
+main (int argc, char *argv[])
+{
+ GdkDisplay *gdkdisplay;
+ Display *xdisplay;
+ GdkScreen *gdkscreen;
+ WnckScreen *screen;
+ cairo_t *cr;
+ gint i, j;
+
+ gtk_init (&argc, &argv);
+
+ gdkdisplay = gdk_display_get_default ();
+ xdisplay = gdk_x11_display_get_xdisplay (gdkdisplay);
+ gdkscreen = gdk_display_get_default_screen (gdkdisplay);
+
+ frame_window_atom = XInternAtom (xdisplay, "_NET_FRAME_WINDOW", FALSE);
+ win_decor_atom = XInternAtom (xdisplay, "_NET_WINDOW_DECOR", FALSE);
+ win_decor_sync_atom = XInternAtom (xdisplay, "_NET_WINDOW_DECOR_SYNC", FALSE);
+ wm_move_resize_atom = XInternAtom (xdisplay, "_NET_WM_MOVERESIZE", FALSE);
+
+ for (i = 0; i < 3; i++)
+ {
+ for (j = 0; j < 3; j++)
+ {
+ if (cursor[i][j].shape != XC_left_ptr)
+ cursor[i][j].cursor =
+ XCreateFontCursor (xdisplay, cursor[i][j].shape);
+ }
+ }
+
+ frame_table = g_hash_table_new (NULL, NULL);
+
+ if (check_dm_hint (gdkscreen))
+ {
+ fprintf (stderr, "%s: Another window decorator is already running\n",
+ argv[0]);
+ return 1;
+ }
+
+ shadow_pixmap = pixmap_new_from_inline (_shadow);
+ large_shadow_pixmap = pixmap_new_from_inline (_large_shadow);
+
+ if (!shadow_pixmap || !large_shadow_pixmap)
+ {
+ fprintf (stderr, "%s, Failed to load shadow images\n", argv[0]);
+ return 1;
+ }
+
+ cr = gdk_cairo_create (GDK_DRAWABLE (large_shadow_pixmap));
+ shadow_pattern = cairo_pattern_create_for_surface (cairo_get_target (cr));
+ cairo_destroy (cr);
+
+ if (!create_tooltip_window ())
+ {
+ fprintf (stderr, "%s, Couldn't create tooltip window\n", argv[0]);
+ return 1;
+ }
+
+ screen = wnck_screen_get_default ();
+
+ gdk_window_add_filter (NULL,
+ event_filter_func,
+ NULL);
+
+ connect_screen (screen);
+
+ if (!init_settings (screen))
+ {
+ fprintf (stderr, "%s: Failed to get necessary gtk settings\n", argv[0]);
+ return 1;
+ }
+
+ set_dm_check_hint (gdk_display_get_default_screen (gdkdisplay));
+
+ gtk_main ();
+
+ return 0;
+}
diff --git a/images/Makefile.am b/images/Makefile.am
new file mode 100644
index 00000000..f3b7aad9
--- /dev/null
+++ b/images/Makefile.am
@@ -0,0 +1,8 @@
+imagesdir = $(imagedir)
+images_DATA = \
+ background.png \
+ window.png
+
+EXTRA_DIST = \
+ background.png \
+ window.png
diff --git a/images/background.png b/images/background.png
new file mode 100644
index 00000000..d2b62315
--- /dev/null
+++ b/images/background.png
Binary files differ
diff --git a/images/window.png b/images/window.png
new file mode 100644
index 00000000..06742e08
--- /dev/null
+++ b/images/window.png
Binary files differ
diff --git a/include/Makefile.am b/include/Makefile.am
new file mode 100644
index 00000000..83727aa4
--- /dev/null
+++ b/include/Makefile.am
@@ -0,0 +1,2 @@
+glxcompincludedir = $(includedir)/compiz
+glxcompinclude_HEADERS = compiz.h
diff --git a/include/compiz.h b/include/compiz.h
new file mode 100644
index 00000000..2840b18c
--- /dev/null
+++ b/include/compiz.h
@@ -0,0 +1,1605 @@
+/*
+ * 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>
+ */
+
+#ifndef _COMPIZ_H
+#define _COMPIZ_H
+
+#include <sys/time.h>
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/extensions/Xdamage.h>
+#include <X11/extensions/sync.h>
+#include <X11/Xregion.h>
+
+#define SN_API_NOT_YET_FROZEN
+#include <libsn/sn.h>
+
+#include <GL/gl.h>
+#include <GL/glx.h>
+
+typedef struct _CompPlugin CompPlugin;
+typedef struct _CompDisplay CompDisplay;
+typedef struct _CompScreen CompScreen;
+typedef struct _CompWindow CompWindow;
+typedef struct _CompTexture CompTexture;
+
+/* virtual modifiers */
+
+#define CompModAlt 0
+#define CompModMeta 1
+#define CompModSuper 2
+#define CompModHyper 3
+#define CompModModeSwitch 4
+#define CompModNumLock 5
+#define CompModScrollLock 6
+#define CompModNum 7
+
+#define CompAltMask (1 << 16)
+#define CompMetaMask (1 << 17)
+#define CompSuperMask (1 << 18)
+#define CompHyperMask (1 << 19)
+#define CompModeSwitchMask (1 << 20)
+#define CompNumLockMask (1 << 21)
+#define CompScrollLockMask (1 << 22)
+
+#define CompPressMask (1 << 23)
+#define CompReleaseMask (1 << 24)
+
+#define CompNoMask (1 << 25)
+
+#define CompWindowProtocolDeleteMask (1 << 0)
+#define CompWindowProtocolTakeFocusMask (1 << 1)
+#define CompWindowProtocolPingMask (1 << 2)
+#define CompWindowProtocolSyncRequestMask (1 << 3)
+
+#define CompWindowTypeDesktopMask (1 << 0)
+#define CompWindowTypeDockMask (1 << 1)
+#define CompWindowTypeToolbarMask (1 << 2)
+#define CompWindowTypeMenuMask (1 << 3)
+#define CompWindowTypeUtilMask (1 << 4)
+#define CompWindowTypeSplashMask (1 << 5)
+#define CompWindowTypeDialogMask (1 << 6)
+#define CompWindowTypeModalDialogMask (1 << 7)
+#define CompWindowTypeNormalMask (1 << 8)
+#define CompWindowTypeFullscreenMask (1 << 9)
+#define CompWindowTypeUnknownMask (1 << 10)
+
+#define CompWindowStateModalMask (1 << 0)
+#define CompWindowStateStickyMask (1 << 1)
+#define CompWindowStateMaximizedVertMask (1 << 2)
+#define CompWindowStateMaximizedHorzMask (1 << 3)
+#define CompWindowStateShadedMask (1 << 4)
+#define CompWindowStateSkipTaskbarMask (1 << 5)
+#define CompWindowStateSkipPagerMask (1 << 6)
+#define CompWindowStateHiddenMask (1 << 7)
+#define CompWindowStateFullscreenMask (1 << 8)
+#define CompWindowStateAboveMask (1 << 9)
+#define CompWindowStateBelowMask (1 << 10)
+#define CompWindowStateDemandsAttentationMask (1 << 11)
+#define CompWindowStateDisplayModalMask (1 << 12)
+
+#define CompWindowActionMoveMask (1 << 0)
+#define CompWindowActionResizeMask (1 << 1)
+#define CompWindowActionStickMask (1 << 2)
+#define CompWindowActionMinimizeMask (1 << 3)
+#define CompWindowActionMaximizeHorzMask (1 << 4)
+#define CompWindowActionMaximizeVertMask (1 << 5)
+#define CompWindowActionFullscreenMask (1 << 6)
+#define CompWindowActionCloseMask (1 << 7)
+
+#define MwmDecorAll (1L << 0)
+#define MwmDecorBorder (1L << 1)
+#define MwmDecorHandle (1L << 2)
+#define MwmDecorTitle (1L << 3)
+#define MwmDecorMenu (1L << 4)
+#define MwmDecorMinimize (1L << 5)
+#define MwmDecorMaximize (1L << 6)
+
+#define WmMoveResizeSizeTopLeft 0
+#define WmMoveResizeSizeTop 1
+#define WmMoveResizeSizeTopRight 2
+#define WmMoveResizeSizeRight 3
+#define WmMoveResizeSizeBottomRight 4
+#define WmMoveResizeSizeBottom 5
+#define WmMoveResizeSizeBottomLeft 6
+#define WmMoveResizeSizeLeft 7
+#define WmMoveResizeMove 8
+#define WmMoveResizeSizeKeyboard 9
+#define WmMoveResizeMoveKeyboard 10
+
+#define OPAQUE 0xffff
+#define COLOR 0xffff
+#define BRIGHT 0xffff
+
+#define RED_SATURATION_WEIGHT 0.30f
+#define GREEN_SATURATION_WEIGHT 0.59f
+#define BLUE_SATURATION_WEIGHT 0.11f
+
+extern char *programName;
+extern char **programArgv;
+extern int programArgc;
+extern char *backgroundImage;
+extern char *windowImage;
+extern REGION emptyRegion;
+extern REGION infiniteRegion;
+extern GLushort defaultColor[4];
+extern Window currentRoot;
+extern Bool testMode;
+extern Bool restartSignal;
+extern CompWindow *lastFoundWindow;
+extern CompWindow *lastDamagedWindow;
+extern Bool replaceCurrentWm;
+
+extern int defaultRefreshRate;
+extern char *defaultTextureFilter;
+
+extern char *windowTypeString[];
+extern int nWindowTypeString;
+
+#define RESTRICT_VALUE(value, min, max) \
+ (((value) < (min)) ? (min): ((value) > (max)) ? (max) : (value))
+
+#define MOD(a,b) ((a) < 0 ? ((b) - ((-(a) - 1) % (b))) - 1 : (a) % (b))
+
+
+/* privates.h */
+
+#define WRAP(priv, real, func, wrapFunc) \
+ (priv)->func = (real)->func; \
+ (real)->func = (wrapFunc)
+
+#define UNWRAP(priv, real, func) \
+ (real)->func = (priv)->func
+
+typedef union _CompPrivate {
+ void *ptr;
+ long val;
+ unsigned long uval;
+ void *(*fptr) (void);
+} CompPrivate;
+
+typedef int (*ReallocPrivatesProc) (int size, void *closure);
+
+int
+allocatePrivateIndex (int *len,
+ char **indices,
+ ReallocPrivatesProc reallocProc,
+ void *closure);
+
+void
+freePrivateIndex (int len,
+ char *indices,
+ int index);
+
+
+/* readpng.c */
+
+Bool
+readPng (const char *filename,
+ char **data,
+ unsigned int *width,
+ unsigned int *height);
+
+Bool
+readPngBuffer (const unsigned char *buffer,
+ char **data,
+ unsigned int *width,
+ unsigned int *height);
+
+/* option.c */
+
+typedef enum {
+ CompOptionTypeBool,
+ CompOptionTypeInt,
+ CompOptionTypeFloat,
+ CompOptionTypeString,
+ CompOptionTypeColor,
+ CompOptionTypeBinding,
+ CompOptionTypeList
+} CompOptionType;
+
+typedef enum {
+ CompBindingTypeKey,
+ CompBindingTypeButton
+} CompBindingType;
+
+typedef struct _CompKeyBinding {
+ int keycode;
+ unsigned int modifiers;
+} CompKeyBinding;
+
+typedef struct _CompButtonBinding {
+ int button;
+ unsigned int modifiers;
+} CompButtonBinding;
+
+typedef struct {
+ CompBindingType type;
+ union {
+ CompKeyBinding key;
+ CompButtonBinding button;
+ } u;
+} CompBinding;
+
+typedef union _CompOptionValue CompOptionValue;
+
+typedef struct {
+ CompOptionType type;
+ CompOptionValue *value;
+ int nValue;
+} CompListValue;
+
+union _CompOptionValue {
+ Bool b;
+ int i;
+ float f;
+ char *s;
+ unsigned short c[4];
+ CompBinding bind;
+ CompListValue list;
+};
+
+typedef struct _CompOptionIntRestriction {
+ int min;
+ int max;
+} CompOptionIntRestriction;
+
+typedef struct _CompOptionFloatRestriction {
+ float min;
+ float max;
+ float precision;
+} CompOptionFloatRestriction;
+
+typedef struct _CompOptionStringRestriction {
+ char **string;
+ int nString;
+} CompOptionStringRestriction;
+
+typedef union {
+ CompOptionIntRestriction i;
+ CompOptionFloatRestriction f;
+ CompOptionStringRestriction s;
+} CompOptionRestriction;
+
+typedef struct _CompOption {
+ char *name;
+ char *shortDesc;
+ char *longDesc;
+ CompOptionType type;
+ CompOptionValue value;
+ CompOptionRestriction rest;
+} CompOption;
+
+typedef CompOption *(*DisplayOptionsProc) (CompDisplay *display, int *count);
+typedef CompOption *(*ScreenOptionsProc) (CompScreen *screen, int *count);
+
+CompOption *
+compFindOption (CompOption *option,
+ int nOption,
+ char *name,
+ int *index);
+
+Bool
+compSetBoolOption (CompOption *option,
+ CompOptionValue *value);
+
+Bool
+compSetIntOption (CompOption *option,
+ CompOptionValue *value);
+
+Bool
+compSetFloatOption (CompOption *option,
+ CompOptionValue *value);
+
+Bool
+compSetStringOption (CompOption *option,
+ CompOptionValue *value);
+
+Bool
+compSetColorOption (CompOption *option,
+ CompOptionValue *value);
+
+Bool
+compSetBindingOption (CompOption *option,
+ CompOptionValue *value);
+
+Bool
+compSetOptionList (CompOption *option,
+ CompOptionValue *value);
+
+unsigned int
+compWindowTypeMaskFromStringList (CompOptionValue *value);
+
+
+/* display.c */
+
+typedef int CompTimeoutHandle;
+
+#define COMP_DISPLAY_OPTION_ACTIVE_PLUGINS 0
+#define COMP_DISPLAY_OPTION_TEXTURE_FILTER 1
+#define COMP_DISPLAY_OPTION_CLICK_TO_FOCUS 2
+#define COMP_DISPLAY_OPTION_AUTORAISE 3
+#define COMP_DISPLAY_OPTION_AUTORAISE_DELAY 4
+#define COMP_DISPLAY_OPTION_NUM 5
+
+typedef CompOption *(*GetDisplayOptionsProc) (CompDisplay *display,
+ int *count);
+typedef Bool (*SetDisplayOptionProc) (CompDisplay *display,
+ char *name,
+ CompOptionValue *value);
+typedef Bool (*SetDisplayOptionForPluginProc) (CompDisplay *display,
+ char *plugin,
+ char *name,
+ CompOptionValue *value);
+
+typedef Bool (*InitPluginForDisplayProc) (CompPlugin *plugin,
+ CompDisplay *display);
+
+typedef void (*FiniPluginForDisplayProc) (CompPlugin *plugin,
+ CompDisplay *display);
+
+typedef void (*HandleEventProc) (CompDisplay *display,
+ XEvent *event);
+
+typedef Bool (*CallBackProc) (void *closure);
+
+typedef void (*ForEachWindowProc) (CompWindow *window,
+ void *closure);
+
+struct _CompDisplay {
+ Display *display;
+ CompScreen *screens;
+
+ char *screenPrivateIndices;
+ int screenPrivateLen;
+
+ int compositeEvent, compositeError, compositeOpcode;
+ int damageEvent, damageError;
+ int randrEvent, randrError;
+ int syncEvent, syncError;
+
+ Bool shapeExtension;
+ int shapeEvent, shapeError;
+
+ SnDisplay *snDisplay;
+
+ Atom supportedAtom;
+ Atom supportingWmCheckAtom;
+
+ Atom utf8StringAtom;
+
+ Atom wmNameAtom;
+
+ Atom winTypeAtom;
+ Atom winTypeDesktopAtom;
+ Atom winTypeDockAtom;
+ Atom winTypeToolbarAtom;
+ Atom winTypeMenuAtom;
+ Atom winTypeUtilAtom;
+ Atom winTypeSplashAtom;
+ Atom winTypeDialogAtom;
+ Atom winTypeNormalAtom;
+
+ Atom winOpacityAtom;
+ Atom winBrightnessAtom;
+ Atom winSaturationAtom;
+ Atom winActiveAtom;
+
+ Atom workareaAtom;
+
+ Atom desktopViewportAtom;
+ Atom desktopGeometryAtom;
+ Atom currentDesktopAtom;
+ Atom numberOfDesktopsAtom;
+
+ Atom winStateAtom;
+ Atom winStateModalAtom;
+ Atom winStateStickyAtom;
+ Atom winStateMaximizedVertAtom;
+ Atom winStateMaximizedHorzAtom;
+ Atom winStateShadedAtom;
+ Atom winStateSkipTaskbarAtom;
+ Atom winStateSkipPagerAtom;
+ Atom winStateHiddenAtom;
+ Atom winStateFullscreenAtom;
+ Atom winStateAboveAtom;
+ Atom winStateBelowAtom;
+ Atom winStateDemandsAttentionAtom;
+ Atom winStateDisplayModalAtom;
+
+ Atom winActionMoveAtom;
+ Atom winActionResizeAtom;
+ Atom winActionStickAtom;
+ Atom winActionMinimizeAtom;
+ Atom winActionMaximizeHorzAtom;
+ Atom winActionMaximizeVertAtom;
+ Atom winActionFullscreenAtom;
+ Atom winActionCloseAtom;
+
+ Atom wmAllowedActionsAtom;
+
+ Atom wmStrutAtom;
+ Atom wmStrutPartialAtom;
+
+ Atom clientListAtom;
+ Atom clientListStackingAtom;
+
+ Atom frameExtentsAtom;
+ Atom frameWindowAtom;
+
+ Atom wmStateAtom;
+ Atom wmChangeStateAtom;
+ Atom wmProtocolsAtom;
+ Atom wmClientLeaderAtom;
+
+ Atom wmDeleteWindowAtom;
+ Atom wmTakeFocusAtom;
+ Atom wmPingAtom;
+ Atom wmSyncRequestAtom;
+
+ Atom wmSyncRequestCounterAtom;
+
+ Atom closeWindowAtom;
+ Atom wmMoveResizeAtom;
+ Atom moveResizeWindowAtom;
+
+ Atom showingDesktopAtom;
+
+ Atom xBackgroundAtom[2];
+
+ Atom panelActionAtom;
+ Atom panelActionMainMenuAtom;
+ Atom panelActionRunDialogAtom;
+
+ Atom mwmHintsAtom;
+
+ Atom managerAtom;
+ Atom targetsAtom;
+ Atom multipleAtom;
+ Atom timestampAtom;
+ Atom versionAtom;
+ Atom atomPairAtom;
+
+ unsigned int lastPing;
+ CompTimeoutHandle pingHandle;
+
+ GLenum textureFilter;
+
+ Window activeWindow;
+
+ Window below;
+ char displayString[256];
+
+ unsigned int modMask[CompModNum];
+ unsigned int ignoredModMask;
+
+ CompOption opt[COMP_DISPLAY_OPTION_NUM];
+
+ CompTimeoutHandle autoRaiseHandle;
+ Window autoRaiseWindow;
+
+ CompOptionValue plugin;
+ Bool dirtyPluginList;
+
+ SetDisplayOptionProc setDisplayOption;
+ SetDisplayOptionForPluginProc setDisplayOptionForPlugin;
+
+ InitPluginForDisplayProc initPluginForDisplay;
+ FiniPluginForDisplayProc finiPluginForDisplay;
+
+ HandleEventProc handleEvent;
+
+ CompPrivate *privates;
+};
+
+extern CompDisplay *compDisplays;
+
+int
+allocateDisplayPrivateIndex (void);
+
+void
+freeDisplayPrivateIndex (int index);
+
+CompOption *
+compGetDisplayOptions (CompDisplay *display,
+ int *count);
+
+CompTimeoutHandle
+compAddTimeout (int time,
+ CallBackProc callBack,
+ void *closure);
+
+void
+compRemoveTimeout (CompTimeoutHandle handle);
+
+int
+compCheckForError (Display *dpy);
+
+Bool
+addDisplay (char *name,
+ char **plugin,
+ int nPlugin);
+
+void
+focusDefaultWindow (CompDisplay *d);
+
+void
+forEachWindowOnDisplay (CompDisplay *display,
+ ForEachWindowProc proc,
+ void *closure);
+
+CompScreen *
+findScreenAtDisplay (CompDisplay *d,
+ Window root);
+
+CompWindow *
+findWindowAtDisplay (CompDisplay *display,
+ Window id);
+
+unsigned int
+virtualToRealModMask (CompDisplay *d,
+ unsigned int modMask);
+
+void
+updateModifierMappings (CompDisplay *d);
+
+void
+eventLoop (void);
+
+void
+handleSelectionRequest (CompDisplay *display,
+ XEvent *event);
+
+void
+handleSelectionClear (CompDisplay *display,
+ XEvent *event);
+
+
+/* event.c */
+
+#define EV_BUTTON(opt, event) \
+ ((opt)->value.bind.type == CompBindingTypeButton && \
+ (opt)->value.bind.u.button.button == (event)->xbutton.button && \
+ ((opt)->value.bind.u.button.modifiers & (event)->xbutton.state) == \
+ (opt)->value.bind.u.button.modifiers)
+
+#define EV_KEY(opt, event) \
+ ((opt)->value.bind.type == CompBindingTypeKey && \
+ (opt)->value.bind.u.key.keycode == (event)->xkey.keycode && \
+ ((opt)->value.bind.u.key.modifiers & (event)->xkey.state) == \
+ (opt)->value.bind.u.key.modifiers)
+
+void
+handleEvent (CompDisplay *display,
+ XEvent *event);
+
+void
+handleSyncAlarm (CompWindow *w);
+
+
+/* paint.c */
+
+#define MULTIPLY_USHORT(us1, us2) \
+ (((GLuint) (us1) * (GLuint) (us2)) / 0xffff)
+
+/* camera distance from screen, 0.5 * tan (FOV) */
+#define DEFAULT_Z_CAMERA 0.866025404f
+
+typedef struct _ScreenPaintAttrib {
+ GLfloat xRotate;
+ GLfloat yRotate;
+ GLfloat vRotate;
+ GLfloat xTranslate;
+ GLfloat yTranslate;
+ GLfloat zTranslate;
+ GLfloat zCamera;
+} ScreenPaintAttrib;
+
+typedef struct _WindowPaintAttrib {
+ GLushort opacity;
+ GLushort brightness;
+ GLushort saturation;
+ GLfloat xScale;
+ GLfloat yScale;
+} WindowPaintAttrib;
+
+extern ScreenPaintAttrib defaultScreenPaintAttrib;
+extern WindowPaintAttrib defaultWindowPaintAttrib;
+
+typedef struct _CompMatrix {
+ float xx; float yx;
+ float xy; float yy;
+ float x0; float y0;
+} CompMatrix;
+
+#define COMP_TEX_COORD_X(m, vx) ((m)->xx * (vx) + (m)->x0)
+#define COMP_TEX_COORD_Y(m, vy) ((m)->yy * (vy) + (m)->y0)
+
+#define COMP_TEX_COORD_XY(m, vx, vy) \
+ ((m)->xx * (vx) + (m)->xy * (vy) + (m)->x0)
+#define COMP_TEX_COORD_YX(m, vx, vy) \
+ ((m)->yx * (vx) + (m)->yy * (vy) + (m)->y0)
+
+
+typedef void (*PreparePaintScreenProc) (CompScreen *screen,
+ int msSinceLastPaint);
+
+typedef void (*DonePaintScreenProc) (CompScreen *screen);
+
+#define PAINT_SCREEN_REGION_MASK (1 << 0)
+#define PAINT_SCREEN_FULL_MASK (1 << 1)
+#define PAINT_SCREEN_TRANSFORMED_MASK (1 << 2)
+#define PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS_MASK (1 << 3)
+
+typedef Bool (*PaintScreenProc) (CompScreen *screen,
+ const ScreenPaintAttrib *sAttrib,
+ Region region,
+ unsigned int mask);
+
+typedef void (*PaintTransformedScreenProc) (CompScreen *screen,
+ const ScreenPaintAttrib *sAttrib,
+ unsigned int mask);
+
+
+#define PAINT_WINDOW_SOLID_MASK (1 << 0)
+#define PAINT_WINDOW_TRANSLUCENT_MASK (1 << 1)
+#define PAINT_WINDOW_TRANSFORMED_MASK (1 << 2)
+#define PAINT_WINDOW_ON_TRANSFORMED_SCREEN_MASK (1 << 3)
+
+typedef Bool (*PaintWindowProc) (CompWindow *window,
+ const WindowPaintAttrib *attrib,
+ Region region,
+ unsigned int mask);
+
+typedef void (*AddWindowGeometryProc) (CompWindow *window,
+ CompMatrix *matrix,
+ int nMatrix,
+ Region region,
+ Region clip);
+
+typedef void (*DrawWindowGeometryProc) (CompWindow *window);
+
+#define PAINT_BACKGROUND_ON_TRANSFORMED_SCREEN_MASK (1 << 0)
+#define PAINT_BACKGROUND_WITH_STENCIL_MASK (1 << 1)
+
+typedef void (*PaintBackgroundProc) (CompScreen *screen,
+ Region region,
+ unsigned int mask);
+
+
+void
+preparePaintScreen (CompScreen *screen,
+ int msSinceLastPaint);
+
+void
+donePaintScreen (CompScreen *screen);
+
+void
+translateRotateScreen (const ScreenPaintAttrib *sa);
+
+void
+paintTransformedScreen (CompScreen *screen,
+ const ScreenPaintAttrib *sAttrib,
+ unsigned int mask);
+
+Bool
+paintScreen (CompScreen *screen,
+ const ScreenPaintAttrib *sAttrib,
+ Region region,
+ unsigned int mask);
+
+Bool
+moreWindowVertices (CompWindow *w,
+ int newSize);
+
+Bool
+moreWindowIndices (CompWindow *w,
+ int newSize);
+
+void
+addWindowGeometry (CompWindow *w,
+ CompMatrix *matrix,
+ int nMatrix,
+ Region region,
+ Region clip);
+
+void
+drawWindowGeometry (CompWindow *w);
+
+void
+drawWindowTexture (CompWindow *w,
+ CompTexture *texture,
+ const WindowPaintAttrib *attrib,
+ unsigned int mask);
+
+Bool
+paintWindow (CompWindow *w,
+ const WindowPaintAttrib *attrib,
+ Region region,
+ unsigned int mask);
+
+void
+paintBackground (CompScreen *screen,
+ Region region,
+ unsigned int mask);
+
+
+/* texture.c */
+
+#define POWER_OF_TWO(v) ((v & (v - 1)) == 0)
+
+typedef enum {
+ COMP_TEXTURE_FILTER_FAST,
+ COMP_TEXTURE_FILTER_GOOD
+} CompTextureFilter;
+
+struct _CompTexture {
+ GLuint name;
+ GLenum target;
+ GLfloat dx, dy;
+ GLXPixmap pixmap;
+ CompTextureFilter filter;
+ GLenum wrap;
+ CompMatrix matrix;
+};
+
+void
+initTexture (CompScreen *screen,
+ CompTexture *texture);
+
+void
+finiTexture (CompScreen *screen,
+ CompTexture *texture);
+
+Bool
+readImageToTexture (CompScreen *screen,
+ CompTexture *texture,
+ char *imageFileName,
+ unsigned int *width,
+ unsigned int *height);
+
+Bool
+readImageBufferToTexture (CompScreen *screen,
+ CompTexture *texture,
+ const unsigned char *imageBuffer,
+ unsigned int *returnWidth,
+ unsigned int *returnHeight);
+
+Bool
+bindPixmapToTexture (CompScreen *screen,
+ CompTexture *texture,
+ Pixmap pixmap,
+ int width,
+ int height,
+ int depth);
+
+void
+releasePixmapFromTexture (CompScreen *screen,
+ CompTexture *texture);
+
+void
+enableTexture (CompScreen *screen,
+ CompTexture *texture,
+ CompTextureFilter filter);
+
+void
+enableTextureClampToBorder (CompScreen *screen,
+ CompTexture *texture,
+ CompTextureFilter filter);
+
+void
+enableTextureClampToEdge (CompScreen *screen,
+ CompTexture *texture,
+ CompTextureFilter filter);
+
+void
+disableTexture (CompTexture *texture);
+
+
+/* screen.c */
+
+#define COMP_SCREEN_OPTION_DETECT_REFRESH_RATE 0
+#define COMP_SCREEN_OPTION_REFRESH_RATE 1
+#define COMP_SCREEN_OPTION_SIZE 2
+#define COMP_SCREEN_OPTION_CLOSE_WINDOW 3
+#define COMP_SCREEN_OPTION_MAIN_MENU 4
+#define COMP_SCREEN_OPTION_RUN_DIALOG 5
+#define COMP_SCREEN_OPTION_COMMAND0 6
+#define COMP_SCREEN_OPTION_RUN_COMMAND0 7
+#define COMP_SCREEN_OPTION_COMMAND1 8
+#define COMP_SCREEN_OPTION_RUN_COMMAND1 9
+#define COMP_SCREEN_OPTION_NUM 10
+
+typedef void (*FuncPtr) (void);
+typedef FuncPtr (*GLXGetProcAddressProc) (const GLubyte *procName);
+
+#ifndef GLX_EXT_render_texture
+#define GLX_TEXTURE_TARGET_EXT 0x6001
+#define GLX_TEXTURE_2D_EXT 0x6002
+#define GLX_TEXTURE_RECTANGLE_EXT 0x6003
+#define GLX_NO_TEXTURE_EXT 0x6004
+#define GLX_FRONT_LEFT_EXT 0x6005
+#endif
+
+typedef Bool (*GLXBindTexImageProc) (Display *display,
+ GLXDrawable drawable,
+ int buffer);
+typedef Bool (*GLXReleaseTexImageProc) (Display *display,
+ GLXDrawable drawable,
+ int buffer);
+typedef void (*GLXQueryDrawableProc) (Display *display,
+ GLXDrawable drawable,
+ int attribute,
+ unsigned int *value);
+
+typedef void (*GLActiveTextureProc) (GLenum texture);
+typedef void (*GLClientActiveTextureProc) (GLenum texture);
+
+
+#define MAX_DEPTH 32
+
+typedef CompOption *(*GetScreenOptionsProc) (CompScreen *screen,
+ int *count);
+typedef Bool (*SetScreenOptionProc) (CompScreen *screen,
+ char *name,
+ CompOptionValue *value);
+typedef Bool (*SetScreenOptionForPluginProc) (CompScreen *screen,
+ char *plugin,
+ char *name,
+ CompOptionValue *value);
+
+typedef Bool (*InitPluginForScreenProc) (CompPlugin *plugin,
+ CompScreen *screen);
+
+typedef void (*FiniPluginForScreenProc) (CompPlugin *plugin,
+ CompScreen *screen);
+
+typedef Bool (*DamageWindowRectProc) (CompWindow *w,
+ Bool initial,
+ BoxPtr rect);
+
+typedef Bool (*DamageWindowRegionProc) (CompWindow *w,
+ Region region);
+
+typedef void (*SetWindowScaleProc) (CompWindow *w,
+ float xScale,
+ float yScale);
+
+typedef Bool (*FocusWindowProc) (CompWindow *window);
+
+typedef void (*WindowResizeNotifyProc) (CompWindow *window);
+
+typedef void (*WindowMoveNotifyProc) (CompWindow *window,
+ int dx,
+ int dy);
+
+#define CompWindowGrabKeyMask (1 << 0)
+#define CompWindowGrabButtonMask (1 << 1)
+#define CompWindowGrabMoveMask (1 << 2)
+#define CompWindowGrabResizeMask (1 << 3)
+
+typedef void (*WindowGrabNotifyProc) (CompWindow *window,
+ int x,
+ int y,
+ unsigned int state,
+ unsigned int mask);
+
+typedef void (*WindowUngrabNotifyProc) (CompWindow *window);
+
+#define COMP_SCREEN_DAMAGE_PENDING_MASK (1 << 0)
+#define COMP_SCREEN_DAMAGE_REGION_MASK (1 << 1)
+#define COMP_SCREEN_DAMAGE_ALL_MASK (1 << 2)
+
+typedef struct _CompKeyGrab {
+ int keycode;
+ unsigned int modifiers;
+ int count;
+} CompKeyGrab;
+
+typedef struct _CompButtonGrab {
+ int button;
+ unsigned int modifiers;
+ int count;
+} CompButtonGrab;
+
+typedef struct _CompGrab {
+ Bool active;
+ Cursor cursor;
+} CompGrab;
+
+typedef struct _CompGroup {
+ struct _CompGroup *next;
+ unsigned int refCnt;
+ Window id;
+} CompGroup;
+
+typedef struct _CompStartupSequence {
+ struct _CompStartupSequence *next;
+ SnStartupSequence *sequence;
+} CompStartupSequence;
+
+struct _CompScreen {
+ CompScreen *next;
+ CompDisplay *display;
+ CompWindow *windows;
+ CompWindow *reverseWindows;
+
+ char *windowPrivateIndices;
+ int windowPrivateLen;
+
+ Colormap colormap;
+ int screenNum;
+ int width;
+ int height;
+ int x;
+ int size;
+ REGION region;
+ Region damage;
+ unsigned long damageMask;
+ Window root;
+ Window fake[2];
+ XWindowAttributes attrib;
+ Window grabWindow;
+ XVisualInfo *glxPixmapVisuals[MAX_DEPTH + 1];
+ int textureRectangle;
+ int textureNonPowerOfTwo;
+ int textureEnvCombine;
+ int textureEnvCrossbar;
+ int textureBorderClamp;
+ int maxTextureUnits;
+ Cursor invisibleCursor;
+ XRectangle *exposeRects;
+ int sizeExpose;
+ int nExpose;
+ CompTexture backgroundTexture;
+ unsigned int pendingDestroys;
+ int desktopWindowCount;
+ KeyCode escapeKeyCode;
+ unsigned int mapNum;
+ unsigned int activeNum;
+
+ SnMonitorContext *snContext;
+ CompStartupSequence *startupSequences;
+ unsigned int startupSequenceTimeoutHandle;
+
+ CompGroup *groups;
+
+ Bool canDoSaturated;
+ Bool canDoSlightlySaturated;
+
+ Window wmSnSelectionWindow;
+ Atom wmSnAtom;
+ Time wmSnTimestamp;
+
+ Cursor normalCursor;
+ Cursor busyCursor;
+
+ CompWindow **clientList;
+ int nClientList;
+
+ CompButtonGrab *buttonGrab;
+ int nButtonGrab;
+ CompKeyGrab *keyGrab;
+ int nKeyGrab;
+
+ CompGrab *grabs;
+ int grabSize;
+ int maxGrab;
+
+ int rasterX;
+ int rasterY;
+ struct timeval lastRedraw;
+ int nextRedraw;
+ int redrawTime;
+ int optimalRedrawTime;
+ int frameStatus;
+
+ GLint stencilRef;
+
+ XRectangle workArea;
+
+ unsigned int showingDesktopMask;
+
+ GLXGetProcAddressProc getProcAddress;
+ GLXBindTexImageProc bindTexImage;
+ GLXReleaseTexImageProc releaseTexImage;
+ GLXQueryDrawableProc queryDrawable;
+
+ GLActiveTextureProc activeTexture;
+ GLClientActiveTextureProc clientActiveTexture;
+
+ GLXContext ctx;
+
+ CompOption opt[COMP_SCREEN_OPTION_NUM];
+
+ SetScreenOptionProc setScreenOption;
+ SetScreenOptionForPluginProc setScreenOptionForPlugin;
+
+ InitPluginForScreenProc initPluginForScreen;
+ FiniPluginForScreenProc finiPluginForScreen;
+
+ PreparePaintScreenProc preparePaintScreen;
+ DonePaintScreenProc donePaintScreen;
+ PaintScreenProc paintScreen;
+ PaintTransformedScreenProc paintTransformedScreen;
+ PaintBackgroundProc paintBackground;
+ PaintWindowProc paintWindow;
+ AddWindowGeometryProc addWindowGeometry;
+ DrawWindowGeometryProc drawWindowGeometry;
+ DamageWindowRectProc damageWindowRect;
+ FocusWindowProc focusWindow;
+ SetWindowScaleProc setWindowScale;
+
+ WindowResizeNotifyProc windowResizeNotify;
+ WindowMoveNotifyProc windowMoveNotify;
+ WindowGrabNotifyProc windowGrabNotify;
+ WindowUngrabNotifyProc windowUngrabNotify;
+
+ CompPrivate *privates;
+};
+
+int
+allocateScreenPrivateIndex (CompDisplay *display);
+
+void
+freeScreenPrivateIndex (CompDisplay *display,
+ int index);
+
+CompOption *
+compGetScreenOptions (CompScreen *screen,
+ int *count);
+
+void
+configureScreen (CompScreen *s,
+ XConfigureEvent *ce);
+
+void
+updateScreenBackground (CompScreen *screen,
+ CompTexture *texture);
+
+void
+detectRefreshRateOfScreen (CompScreen *s);
+
+Bool
+addScreen (CompDisplay *display,
+ int screenNum,
+ Window wmSnSelectionWindow,
+ Atom wmSnAtom,
+ Time wmSnTimestamp);
+
+void
+damageScreenRegion (CompScreen *screen,
+ Region region);
+
+void
+damageScreen (CompScreen *screen);
+
+void
+damagePendingOnScreen (CompScreen *s);
+
+void
+insertWindowIntoScreen (CompScreen *s,
+ CompWindow *w,
+ Window aboveId);
+
+void
+unhookWindowFromScreen (CompScreen *s,
+ CompWindow *w);
+
+void
+forEachWindowOnScreen (CompScreen *screen,
+ ForEachWindowProc proc,
+ void *closure);
+
+CompWindow *
+findWindowAtScreen (CompScreen *s,
+ Window id);
+
+CompWindow *
+findTopLevelWindowAtScreen (CompScreen *s,
+ Window id);
+
+int
+pushScreenGrab (CompScreen *s,
+ Cursor cursor);
+
+void
+removeScreenGrab (CompScreen *s,
+ int index,
+ XPoint *restorePointer);
+
+Bool
+addScreenBinding (CompScreen *s,
+ CompBinding *binding);
+
+void
+removeScreenBinding (CompScreen *s,
+ CompBinding *binding);
+
+void
+updatePassiveGrabs (CompScreen *s);
+
+void
+updateWorkareaForScreen (CompScreen *s);
+
+void
+updateClientListForScreen (CompScreen *s);
+
+Window
+getActiveWindow (CompDisplay *display,
+ Window root);
+
+void
+closeActiveWindow (CompScreen *s);
+
+void
+panelAction (CompScreen *s,
+ Atom panelAction);
+
+void
+runCommand (CompScreen *s,
+ const char *command);
+
+void
+moveScreenViewport (CompScreen *s,
+ int tx,
+ Bool sync);
+
+void
+moveWindowToViewportPosition (CompWindow *w,
+ int x,
+ Bool sync);
+
+CompGroup *
+addGroupToScreen (CompScreen *s,
+ Window id);
+void
+removeGroupFromScreen (CompScreen *s,
+ CompGroup *group);
+
+CompGroup *
+findGroupAtScreen (CompScreen *s,
+ Window id);
+
+void
+applyStartupProperties (CompScreen *screen,
+ CompWindow *window);
+
+void
+enterShowDesktopMode (CompScreen *s);
+
+void
+leaveShowDesktopMode (CompScreen *s);
+
+void
+sendWindowActivationRequest (CompScreen *s,
+ Window id);
+
+/* window.c */
+
+#define WINDOW_INVISIBLE(w) \
+ ((w)->attrib.map_state != IsViewable || \
+ (!(w)->damaged) || \
+ (w)->attrib.x + (w)->width + (w)->output.right <= 0 || \
+ (w)->attrib.y + (w)->height + (w)->output.bottom <= 0 || \
+ (w)->attrib.x - (w)->output.left >= (w)->screen->width || \
+ (w)->attrib.y - (w)->output.top >= (w)->screen->height)
+
+typedef Bool (*InitPluginForWindowProc) (CompPlugin *plugin,
+ CompWindow *window);
+typedef void (*FiniPluginForWindowProc) (CompPlugin *plugin,
+ CompWindow *window);
+
+typedef struct _CompWindowExtents {
+ int left;
+ int right;
+ int top;
+ int bottom;
+} CompWindowExtents;
+
+typedef struct _CompStruts {
+ XRectangle left;
+ XRectangle right;
+ XRectangle top;
+ XRectangle bottom;
+} CompStruts;
+
+struct _CompWindow {
+ CompScreen *screen;
+ CompWindow *next;
+ CompWindow *prev;
+
+ int refcnt;
+ Window id;
+ Window frame;
+ unsigned int mapNum;
+ unsigned int activeNum;
+ XWindowAttributes attrib;
+ int serverX;
+ int serverY;
+ Window transientFor;
+ Window clientLeader;
+ XSizeHints sizeHints;
+ Pixmap pixmap;
+ CompTexture texture;
+ CompMatrix matrix;
+ Damage damage;
+ Bool inputHint;
+ Bool alpha;
+ GLint width;
+ GLint height;
+ Region region;
+ Region clip;
+ unsigned int wmType;
+ unsigned int type;
+ unsigned int state;
+ unsigned int actions;
+ unsigned int protocols;
+ unsigned int mwmDecor;
+ Bool invisible;
+ Bool destroyed;
+ Bool damaged;
+ int destroyRefCnt;
+ int unmapRefCnt;
+
+ Bool placed;
+ Bool minimized;
+
+ char *startupId;
+ char *resName;
+ char *resClass;
+
+ CompGroup *group;
+
+ unsigned int lastPong;
+ Bool alive;
+ GLushort saturation;
+
+ WindowPaintAttrib paint;
+ Bool scaled;
+
+ CompWindowExtents input;
+ CompWindowExtents output;
+
+ CompStruts *struts;
+
+ XWindowChanges saveWc;
+ int saveMask;
+
+ XSyncCounter syncCounter;
+ XSyncValue syncValue;
+ XSyncAlarm syncAlarm;
+ unsigned long syncAlarmConnection;
+ unsigned int syncWaitHandle;
+
+ Bool syncWait;
+ int syncX;
+ int syncY;
+ int syncWidth;
+ int syncHeight;
+ int syncBorderWidth;
+
+ XRectangle *damageRects;
+ int sizeDamage;
+ int nDamage;
+
+ GLfloat *vertices;
+ int vertexSize;
+ GLushort *indices;
+ int indexSize;
+ int vCount;
+ int texUnits;
+
+ CompPrivate *privates;
+};
+
+int
+allocateWindowPrivateIndex (CompScreen *screen);
+
+void
+freeWindowPrivateIndex (CompScreen *screen,
+ int index);
+
+unsigned int
+windowStateMask (CompDisplay *display,
+ Atom state);
+
+unsigned int
+getWindowState (CompDisplay *display,
+ Window id);
+
+void
+setWindowState (CompDisplay *display,
+ unsigned int state,
+ Window id);
+
+unsigned int
+getWindowType (CompDisplay *display,
+ Window id);
+
+void
+recalcWindowType (CompWindow *w);
+
+unsigned int
+getMwmDecor (CompDisplay *display,
+ Window id);
+
+unsigned int
+getProtocols (CompDisplay *display,
+ Window id);
+
+unsigned short
+getWindowProp32 (CompDisplay *display,
+ Window id,
+ Atom property,
+ unsigned short defaultValue);
+
+void
+setWindowProp32 (CompDisplay *display,
+ Window id,
+ Atom property,
+ unsigned short value);
+
+void
+updateNormalHints (CompWindow *window);
+
+void
+updateWmHints (CompWindow *w);
+
+void
+updateWindowClassHints (CompWindow *window);
+
+Window
+getClientLeader (CompWindow *w);
+
+int
+getWmState (CompDisplay *display,
+ Window id);
+
+void
+setWmState (CompDisplay *display,
+ int state,
+ Window id);
+
+void
+setWindowFrameExtents (CompWindow *w,
+ CompWindowExtents *input,
+ CompWindowExtents *output);
+
+void
+updateWindowRegion (CompWindow *w);
+
+Bool
+updateWindowStruts (CompWindow *w);
+
+void
+addWindow (CompScreen *screen,
+ Window id,
+ Window aboveId);
+
+void
+removeWindow (CompWindow *w);
+
+void
+destroyWindow (CompWindow *w);
+
+void
+mapWindow (CompWindow *w);
+
+void
+unmapWindow (CompWindow *w);
+
+void
+bindWindow (CompWindow *w);
+
+void
+releaseWindow (CompWindow *w);
+
+void
+moveWindow (CompWindow *w,
+ int dx,
+ int dy,
+ Bool damage);
+
+void
+syncWindowPosition (CompWindow *w);
+
+void
+sendSyncRequest (CompWindow *w);
+
+Bool
+resizeWindow (CompWindow *w,
+ int x,
+ int y,
+ int width,
+ int height,
+ int borderWidth);
+
+void
+configureWindow (CompWindow *w,
+ XConfigureEvent *ce);
+
+void
+circulateWindow (CompWindow *w,
+ XCirculateEvent *ce);
+
+void
+addWindowDamage (CompWindow *w);
+
+void
+damageWindowOutputExtents (CompWindow *w);
+
+Bool
+damageWindowRect (CompWindow *w,
+ Bool initial,
+ BoxPtr rect);
+
+void
+damageWindowRegion (CompWindow *w,
+ Region region);
+
+void
+setWindowScale (CompWindow *w,
+ float xScale,
+ float yScale);
+
+Bool
+focusWindow (CompWindow *w);
+
+void
+windowResizeNotify (CompWindow *w);
+
+void
+windowMoveNotify (CompWindow *w,
+ int dx,
+ int dy);
+
+void
+windowGrabNotify (CompWindow *w,
+ int x,
+ int y,
+ unsigned int state,
+ unsigned int mask);
+
+void
+windowUngrabNotify (CompWindow *w);
+
+void
+moveInputFocusToWindow (CompWindow *w);
+
+void
+updateWindowAttributes (CompWindow *w);
+
+void
+activateWindow (CompWindow *w);
+
+void
+closeWindow (CompWindow *w);
+
+void
+getOuterRectOfWindow (CompWindow *w,
+ XRectangle *r);
+
+Bool
+constrainNewWindowSize (CompWindow *w,
+ int width,
+ int height,
+ int *newWidth,
+ int *newHeight);
+
+void
+hideWindow (CompWindow *w);
+
+void
+showWindow (CompWindow *w);
+
+void
+minimizeWindow (CompWindow *w);
+
+void
+unminimizeWindow (CompWindow *w);
+
+
+/* plugin.c */
+
+typedef Bool (*InitPluginProc) (CompPlugin *plugin);
+typedef void (*FiniPluginProc) (CompPlugin *plugin);
+
+typedef enum {
+ CompPluginRuleBefore,
+ CompPluginRuleAfter
+} CompPluginRule;
+
+typedef struct _CompPluginDep {
+ CompPluginRule rule;
+ char *plugin;
+} CompPluginDep;
+
+typedef struct _CompPluginVTable {
+ char *name;
+ char *shortDesc;
+ char *longDesc;
+
+ InitPluginProc init;
+ FiniPluginProc fini;
+
+ InitPluginForDisplayProc initDisplay;
+ FiniPluginForDisplayProc finiDisplay;
+
+ InitPluginForScreenProc initScreen;
+ FiniPluginForScreenProc finiScreen;
+
+ InitPluginForWindowProc initWindow;
+ FiniPluginForWindowProc finiWindow;
+
+ GetDisplayOptionsProc getDisplayOptions;
+ SetDisplayOptionProc setDisplayOption;
+ GetScreenOptionsProc getScreenOptions;
+ SetScreenOptionProc setScreenOption;
+
+ CompPluginDep *deps;
+ int nDeps;
+} CompPluginVTable;
+
+typedef CompPluginVTable *(*PluginGetInfoProc) (void);
+
+struct _CompPlugin {
+ CompPlugin *next;
+ void *dlhand;
+ CompPluginVTable *vTable;
+};
+
+CompPluginVTable *
+getCompPluginInfo (void);
+
+void
+screenInitPlugins (CompScreen *s);
+
+void
+screenFiniPlugins (CompScreen *s);
+
+void
+windowInitPlugins (CompWindow *w);
+
+void
+windowFiniPlugins (CompWindow *w);
+
+CompPlugin *
+findActivePlugin (char *name);
+
+CompPlugin *
+loadPlugin (char *plugin);
+
+void
+unloadPlugin (CompPlugin *p);
+
+Bool
+pushPlugin (CompPlugin *p);
+
+CompPlugin *
+popPlugin (void);
+
+#endif
diff --git a/kde/Makefile.am b/kde/Makefile.am
new file mode 100644
index 00000000..d0f733bd
--- /dev/null
+++ b/kde/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = window-decorator
diff --git a/kde/window-decorator/Makefile.am b/kde/window-decorator/Makefile.am
new file mode 100644
index 00000000..53908d78
--- /dev/null
+++ b/kde/window-decorator/Makefile.am
@@ -0,0 +1,7 @@
+if USE_KDE
+kde_window_decorator_program =
+endif
+
+INCLUDES = @KDE_WINDOW_DECORATOR_CFLAGS@
+
+bin_PROGRAMS = $(kde_window_decorator_program)
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
new file mode 100644
index 00000000..486fefe6
--- /dev/null
+++ b/plugins/Makefile.am
@@ -0,0 +1,76 @@
+libfade_la_LDFLAGS = -module -avoid-version -no-undefined
+libfade_la_LIBADD = @COMPIZ_LIBS@
+libfade_la_SOURCES = fade.c
+
+libcube_la_LDFLAGS = -module -avoid-version -no-undefined
+libcube_la_LIBADD = @COMPIZ_LIBS@ @LIBSVG_CAIRO_LIBS@
+libcube_la_SOURCES = cube.c
+
+librotate_la_LDFLAGS = -module -avoid-version -no-undefined
+librotate_la_LIBADD = @COMPIZ_LIBS@
+librotate_la_SOURCES = rotate.c
+
+libzoom_la_LDFLAGS = -module -avoid-version -no-undefined
+libzoom_la_LIBADD = @COMPIZ_LIBS@
+libzoom_la_SOURCES = zoom.c
+
+libscale_la_LDFLAGS = -module -avoid-version -no-undefined
+libscale_la_LIBADD = @COMPIZ_LIBS@
+libscale_la_SOURCES = scale.c
+
+libwobbly_la_LDFLAGS = -module -avoid-version -no-undefined
+libwobbly_la_LIBADD = @COMPIZ_LIBS@
+libwobbly_la_SOURCES = wobbly.c
+
+libminimize_la_LDFLAGS = -module -avoid-version -no-undefined
+libminimize_la_LIBADD = @COMPIZ_LIBS@
+libminimize_la_SOURCES = minimize.c
+
+libmove_la_LDFLAGS = -module -avoid-version -no-undefined
+libmove_la_LIBADD = @COMPIZ_LIBS@
+libmove_la_SOURCES = move.c
+
+libresize_la_LDFLAGS = -module -avoid-version -no-undefined
+libresize_la_LIBADD = @COMPIZ_LIBS@
+libresize_la_SOURCES = resize.c
+
+libdecoration_la_LDFLAGS = -module -avoid-version -no-undefined
+libdecoration_la_LIBADD = @COMPIZ_LIBS@
+libdecoration_la_SOURCES = decoration.c
+
+if GCONF_PLUGIN
+libgconf_la_LDFLAGS = -module -avoid-version -no-undefined
+libgconf_la_LIBADD = @COMPIZ_LIBS@ @GCONF_LIBS@
+libgconf_la_SOURCES = gconf.c
+libgconf_module = libgconf.la
+endif
+
+if PLACE_PLUGIN
+libplace_la_LDFLAGS = -module -avoid-version -no-undefined
+libplace_la_LIBADD = @COMPIZ_LIBS@ @PLACE_LIBS@
+libplace_la_SOURCES = place.c
+libplace_module = libplace.la
+endif
+
+INCLUDES = \
+ @COMPIZ_CFLAGS@ \
+ @LIBSVG_CAIRO_CFLAGS@ \
+ @GCONF_CFLAGS@ \
+ @PLACE_CFLAGS@ \
+ -I$(top_srcdir)/include
+
+moduledir = $(plugindir)
+
+module_LTLIBRARIES = \
+ libfade.la \
+ libcube.la \
+ librotate.la \
+ libzoom.la \
+ libscale.la \
+ libwobbly.la \
+ libminimize.la \
+ libmove.la \
+ libresize.la \
+ libdecoration.la \
+ $(libgconf_module) \
+ $(libplace_module)
diff --git a/plugins/cube.c b/plugins/cube.c
new file mode 100644
index 00000000..381c96e1
--- /dev/null
+++ b/plugins/cube.c
@@ -0,0 +1,831 @@
+/*
+ * 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
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+#include <sys/time.h>
+
+#ifdef USE_LIBSVG_CAIRO
+#include <cairo-xlib.h>
+#include <svg-cairo.h>
+#endif
+
+#include <X11/Xatom.h>
+#include <X11/Xproto.h>
+
+#include <compiz.h>
+
+#define CUBE_COLOR_RED_DEFAULT 0xefef
+#define CUBE_COLOR_GREEN_DEFAULT 0xebeb
+#define CUBE_COLOR_BLUE_DEFAULT 0xe7e7
+
+#define CUBE_IN_DEFAULT FALSE
+
+#define CUBE_NEXT_KEY_DEFAULT "space"
+#define CUBE_NEXT_MODIFIERS_DEFAULT CompPressMask
+
+#define CUBE_PREV_KEY_DEFAULT "BackSpace"
+#define CUBE_PREV_MODIFIERS_DEFAULT CompPressMask
+
+static int displayPrivateIndex;
+
+typedef struct _CubeDisplay {
+ int screenPrivateIndex;
+ HandleEventProc handleEvent;
+} CubeDisplay;
+
+#define CUBE_SCREEN_OPTION_COLOR 0
+#define CUBE_SCREEN_OPTION_IN 1
+#define CUBE_SCREEN_OPTION_SVGS 2
+#define CUBE_SCREEN_OPTION_NEXT 3
+#define CUBE_SCREEN_OPTION_PREV 4
+#define CUBE_SCREEN_OPTION_NUM 5
+
+typedef struct _CubeScreen {
+ PaintTransformedScreenProc paintTransformedScreen;
+ PaintBackgroundProc paintBackground;
+ SetScreenOptionProc setScreenOption;
+
+ CompOption opt[CUBE_SCREEN_OPTION_NUM];
+
+ int invert;
+ int xrotations;
+ GLfloat distance;
+ Bool paintTopBottom;
+ GLushort color[3];
+ GLfloat tc[12];
+
+ GLfloat *vertices;
+ int nvertices;
+
+ Pixmap pixmap;
+ int pw, ph;
+ CompTexture texture;
+
+#ifdef USE_LIBSVG_CAIRO
+ cairo_t *cr;
+ svg_cairo_t *svgc;
+ int svgNFile;
+ int svgCurFile;
+ CompOptionValue *svgFiles;
+#endif
+
+} CubeScreen;
+
+#define GET_CUBE_DISPLAY(d) \
+ ((CubeDisplay *) (d)->privates[displayPrivateIndex].ptr)
+
+#define CUBE_DISPLAY(d) \
+ CubeDisplay *cd = GET_CUBE_DISPLAY (d)
+
+#define GET_CUBE_SCREEN(s, cd) \
+ ((CubeScreen *) (s)->privates[(cd)->screenPrivateIndex].ptr)
+
+#define CUBE_SCREEN(s) \
+ CubeScreen *cs = GET_CUBE_SCREEN (s, GET_CUBE_DISPLAY (s->display))
+
+#define NUM_OPTIONS(s) (sizeof ((s)->opt) / sizeof (CompOption))
+
+#ifdef USE_LIBSVG_CAIRO
+static void
+cubeInitSvg (CompScreen *s)
+
+{
+ CUBE_SCREEN (s);
+
+ cs->pixmap = None;
+ cs->pw = cs->ph = 0;
+ cs->cr = 0;
+ cs->svgc = 0;
+}
+
+static void
+cubeFiniSvg (CompScreen *s)
+
+{
+ CUBE_SCREEN (s);
+
+ if (cs->svgc)
+ svg_cairo_destroy (cs->svgc);
+
+ if (cs->cr)
+ cairo_destroy (cs->cr);
+
+ if (cs->pixmap)
+ XFreePixmap (s->display->display, cs->pixmap);
+}
+
+static void
+cubeLoadSvg (CompScreen *s,
+ int n)
+{
+ unsigned int width, height;
+
+ CUBE_SCREEN (s);
+
+ if (!cs->svgNFile || cs->pw != s->width || cs->ph != s->height)
+ {
+ finiTexture (s, &cs->texture);
+ initTexture (s, &cs->texture);
+ cubeFiniSvg (s);
+ cubeInitSvg (s);
+
+ if (!cs->svgNFile)
+ return;
+ }
+
+ if (!cs->pixmap)
+ {
+ cairo_surface_t *surface;
+ Visual *visual;
+ int depth;
+
+ cs->pw = s->width;
+ cs->ph = s->height;
+
+ depth = DefaultDepth (s->display->display, s->screenNum);
+ cs->pixmap = XCreatePixmap (s->display->display, s->root,
+ s->width, s->height,
+ depth);
+
+ if (!bindPixmapToTexture (s, &cs->texture, cs->pixmap,
+ s->width, s->height, depth))
+ {
+ fprintf (stderr, "%s: Couldn't bind slide pixmap 0x%x to "
+ "texture\n", programName, (int) cs->pixmap);
+ }
+
+ if (cs->texture.target == GL_TEXTURE_RECTANGLE_ARB)
+ {
+ cs->tc[0] = s->width / 2.0f;
+ cs->tc[1] = s->height / 2.0f;
+
+ cs->tc[2] = s->width;
+ cs->tc[3] = s->height;
+
+ cs->tc[4] = 0.0f;
+ cs->tc[5] = s->height;
+
+ cs->tc[6] = 0.0f;
+ cs->tc[7] = 0.0f;
+
+ cs->tc[8] = s->width;
+ cs->tc[9] = 0.0f;
+
+ cs->tc[10] = s->width;
+ cs->tc[11] = s->height;
+ }
+ else
+ {
+ cs->tc[0] = 0.5f;
+ cs->tc[1] = 0.5f;
+
+ cs->tc[2] = 1.0f;
+ cs->tc[3] = 1.0f;
+
+ cs->tc[4] = 0.0f;
+ cs->tc[5] = 1.0f;
+
+ cs->tc[6] = 0.0f;
+ cs->tc[7] = 0.0f;
+
+ cs->tc[8] = 1.0f;
+ cs->tc[9] = 0.0f;
+
+ cs->tc[10] = 1.0;
+ cs->tc[11] = 1.0f;
+ }
+
+ visual = DefaultVisual (s->display->display, s->screenNum);
+ surface = cairo_xlib_surface_create (s->display->display,
+ cs->pixmap, visual,
+ s->width, s->height);
+ cs->cr = cairo_create (surface);
+ cairo_surface_destroy (surface);
+ }
+
+ cs->svgCurFile = n % cs->svgNFile;
+
+ if (cs->svgc)
+ svg_cairo_destroy (cs->svgc);
+
+ if (svg_cairo_create (&cs->svgc))
+ {
+ fprintf (stderr, "%s: Failed to create svg_cairo_t.\n",
+ programName);
+ return;
+ }
+
+ svg_cairo_set_viewport_dimension (cs->svgc, s->width, s->height);
+
+ if (svg_cairo_parse (cs->svgc, cs->svgFiles[cs->svgCurFile].s))
+ {
+ fprintf (stderr, "%s: Failed to load svg: %s.\n",
+ programName, cs->svgFiles[cs->svgCurFile].s);
+ return;
+ }
+
+ svg_cairo_get_size (cs->svgc, &width, &height);
+
+ cairo_save (cs->cr);
+ cairo_set_source_rgb (cs->cr,
+ (double) cs->color[0] / 0xffff,
+ (double) cs->color[1] / 0xffff,
+ (double) cs->color[2] / 0xffff);
+ cairo_rectangle (cs->cr, 0, 0, s->width, s->height);
+ cairo_fill (cs->cr);
+
+ cairo_scale (cs->cr,
+ (double) s->width / width,
+ (double) s->height / height);
+
+ svg_cairo_render (cs->svgc, cs->cr);
+ cairo_restore (cs->cr);
+}
+#endif
+
+static Bool
+cubeUpdateGeometry (CompScreen *s,
+ int sides,
+ Bool invert)
+{
+ GLfloat radius, distance;
+ GLfloat *v;
+ int i, n;
+
+ CUBE_SCREEN (s);
+
+ 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 CompOption *
+cubeGetScreenOptions (CompScreen *screen,
+ int *count)
+{
+ CUBE_SCREEN (screen);
+
+ *count = NUM_OPTIONS (cs);
+ return cs->opt;
+}
+
+static Bool
+cubeSetScreenOption (CompScreen *screen,
+ 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->size, o->value.b ? -1 : 1))
+ return TRUE;
+ }
+ break;
+ case CUBE_SCREEN_OPTION_SVGS:
+ if (compSetOptionList (o, value))
+ {
+
+#ifdef USE_LIBSVG_CAIRO
+ cs->svgFiles = cs->opt[CUBE_SCREEN_OPTION_SVGS].value.list.value;
+ cs->svgNFile = cs->opt[CUBE_SCREEN_OPTION_SVGS].value.list.nValue;
+
+ cubeLoadSvg (screen, cs->svgCurFile);
+ damageScreen (screen);
+#endif
+
+ return TRUE;
+ }
+ break;
+ case CUBE_SCREEN_OPTION_NEXT:
+ case CUBE_SCREEN_OPTION_PREV:
+ if (compSetBindingOption (o, value))
+ return TRUE;
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+static void
+cubeScreenInitOptions (CubeScreen *cs,
+ Display *display)
+{
+ CompOption *o;
+
+ o = &cs->opt[CUBE_SCREEN_OPTION_COLOR];
+ o->name = "color";
+ o->shortDesc = "Cube Color";
+ o->longDesc = "Color of top and bottom sides of the cube";
+ o->type = CompOptionTypeColor;
+ o->value.c[0] = CUBE_COLOR_RED_DEFAULT;
+ o->value.c[1] = CUBE_COLOR_GREEN_DEFAULT;
+ o->value.c[2] = CUBE_COLOR_BLUE_DEFAULT;
+ o->value.c[3] = 0xffff;
+
+ o = &cs->opt[CUBE_SCREEN_OPTION_IN];
+ o->name = "in";
+ o->shortDesc = "Inside Cube";
+ o->longDesc = "Inside cube";
+ o->type = CompOptionTypeBool;
+ o->value.b = CUBE_IN_DEFAULT;
+
+ o = &cs->opt[CUBE_SCREEN_OPTION_SVGS];
+ o->name = "svgs";
+ o->shortDesc = "SVG files";
+ o->longDesc = "List of SVG files rendered on top face of cube";
+ o->type = CompOptionTypeList;
+ o->value.list.type = CompOptionTypeString;
+ o->value.list.nValue = 0;
+ o->value.list.value = 0;
+ o->rest.s.string = 0;
+ o->rest.s.nString = 0;
+
+ o = &cs->opt[CUBE_SCREEN_OPTION_NEXT];
+ o->name = "next_slide";
+ o->shortDesc = "Next Slide";
+ o->longDesc = "Adavence to next slide";
+ o->type = CompOptionTypeBinding;
+ o->value.bind.type = CompBindingTypeKey;
+ o->value.bind.u.key.modifiers = CUBE_NEXT_MODIFIERS_DEFAULT;
+ o->value.bind.u.key.keycode =
+ XKeysymToKeycode (display, XStringToKeysym (CUBE_NEXT_KEY_DEFAULT));
+
+ o = &cs->opt[CUBE_SCREEN_OPTION_PREV];
+ o->name = "prev_slide";
+ o->shortDesc = "Previous Slide";
+ o->longDesc = "Go back to previous slide";
+ o->type = CompOptionTypeBinding;
+ o->value.bind.type = CompBindingTypeKey;
+ o->value.bind.u.key.modifiers = CUBE_PREV_MODIFIERS_DEFAULT;
+ o->value.bind.u.key.keycode =
+ XKeysymToKeycode (display, XStringToKeysym (CUBE_PREV_KEY_DEFAULT));
+}
+
+static void
+cubePaintTransformedScreen (CompScreen *s,
+ const ScreenPaintAttrib *sAttrib,
+ unsigned int mask)
+{
+ ScreenPaintAttrib sa = *sAttrib;
+ int xMove = 0;
+
+ CUBE_SCREEN (s);
+
+ if (mask & PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS_MASK)
+ glClear (GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+ else
+ glClear (GL_COLOR_BUFFER_BIT);
+
+ if (sa.vRotate > 100.0f)
+ sa.vRotate = 100.0f;
+ else if (sAttrib->vRotate < -100.0f)
+ sa.vRotate = -100.0f;
+ else
+ sa.vRotate = sAttrib->vRotate;
+
+ UNWRAP (cs, s, paintTransformedScreen);
+
+ sa.xTranslate = sAttrib->xTranslate;
+ sa.yTranslate = sAttrib->yTranslate;
+ sa.zTranslate = -cs->invert * cs->distance;
+
+ sa.xRotate = sAttrib->xRotate * cs->invert;
+ if (sa.xRotate > 0.0f)
+ {
+ cs->xrotations = (int) (s->size * sa.xRotate) / 360;
+ sa.xRotate = sa.xRotate - (360.0f * cs->xrotations) / s->size;
+ }
+ else
+ {
+ cs->xrotations = (int) (s->size * sa.xRotate) / 360;
+ sa.xRotate = sa.xRotate -
+ (360.0f * cs->xrotations) / s->size + 360.0f / s->size;
+ cs->xrotations--;
+ }
+
+ if (cs->invert != 1 || sa.vRotate != 0.0f || sa.yTranslate != 0.0f)
+ {
+ glColor3usv (cs->color);
+
+ glPushMatrix ();
+
+ if (sAttrib->xRotate > 0.0f)
+ {
+ sa.yRotate += 360.0f / s->size;
+ translateRotateScreen (&sa);
+ sa.yRotate -= 360.0f / s->size;
+ }
+ else
+ translateRotateScreen (&sa);
+
+ glVertexPointer (3, GL_FLOAT, 0, cs->vertices);
+
+ if (cs->invert == 1 && s->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 (&cs->texture);
+ }
+ else
+ glDrawArrays (GL_TRIANGLE_FAN, 0, cs->nvertices >> 1);
+
+ glDrawArrays (GL_TRIANGLE_FAN, cs->nvertices >> 1,
+ cs->nvertices >> 1);
+
+ glPopMatrix ();
+ }
+
+ /* outside 4 side cube */
+ if (s->size == 4 && cs->invert == 1)
+ {
+ if (sAttrib->xRotate != 0.0f)
+ {
+ xMove = cs->xrotations;
+
+ moveScreenViewport (s, xMove, FALSE);
+ (*s->paintTransformedScreen) (s, &sa, mask);
+ moveScreenViewport (s, -xMove, FALSE);
+
+ xMove++;
+
+ moveScreenViewport (s, xMove, FALSE);
+ }
+
+ sa.yRotate -= 360.0f / s->size;
+
+ (*s->paintTransformedScreen) (s, &sa, mask);
+
+ moveScreenViewport (s, -xMove, FALSE);
+ }
+ else
+ {
+ if (sa.xRotate > 180.0f / s->size)
+ {
+ sa.yRotate -= 360.0f / s->size;
+ cs->xrotations++;
+ }
+
+ sa.yRotate -= 360.0f / s->size;
+ xMove = -1 - cs->xrotations;
+
+ moveScreenViewport (s, xMove, FALSE);
+ (*s->paintTransformedScreen) (s, &sa, mask);
+ moveScreenViewport (s, -xMove, FALSE);
+
+ sa.yRotate += 360.0f / s->size;
+ xMove = -cs->xrotations;
+
+ moveScreenViewport (s, xMove, FALSE);
+ (*s->paintTransformedScreen) (s, &sa, mask);
+ moveScreenViewport (s, -xMove, FALSE);
+
+ sa.yRotate += 360.0f / s->size;
+ xMove = 1 - cs->xrotations;
+
+ moveScreenViewport (s, xMove, FALSE);
+ (*s->paintTransformedScreen) (s, &sa, mask);
+ moveScreenViewport (s, -xMove, FALSE);
+ }
+
+ WRAP (cs, s, paintTransformedScreen, cubePaintTransformedScreen);
+}
+
+static void
+cubePaintBackground (CompScreen *s,
+ Region region,
+ unsigned int mask)
+{
+ CUBE_SCREEN (s);
+
+ s->stencilRef++;
+
+ UNWRAP (cs, s, paintBackground);
+ (*s->paintBackground) (s, region, mask);
+ WRAP (cs, s, paintBackground, cubePaintBackground);
+}
+
+#ifdef USE_LIBSVG_CAIRO
+static void
+cubeHandleEvent (CompDisplay *d,
+ XEvent *event)
+{
+ CompScreen *s;
+
+ CUBE_DISPLAY (d);
+
+ switch (event->type) {
+ case KeyPress:
+ case KeyRelease:
+ s = findScreenAtDisplay (d, event->xkey.root);
+ if (s)
+ {
+ CUBE_SCREEN (s);
+
+ if (EV_KEY (&cs->opt[CUBE_SCREEN_OPTION_NEXT], event))
+ {
+ cubeLoadSvg (s, (cs->svgCurFile + 1) % cs->svgNFile);
+ damageScreen (s);
+ }
+
+ if (EV_KEY (&cs->opt[CUBE_SCREEN_OPTION_PREV], event))
+ {
+ cubeLoadSvg (s, (cs->svgCurFile - 1 + cs->svgNFile) %
+ cs->svgNFile);
+ damageScreen (s);
+ }
+ }
+ break;
+ case ButtonPress:
+ case ButtonRelease:
+ s = findScreenAtDisplay (d, event->xbutton.root);
+ if (s)
+ {
+ CUBE_SCREEN (s);
+
+ if (EV_BUTTON (&cs->opt[CUBE_SCREEN_OPTION_NEXT], event))
+ {
+ cubeLoadSvg (s, (cs->svgCurFile + 1) % cs->svgNFile);
+ damageScreen (s);
+ }
+
+ if (EV_BUTTON (&cs->opt[CUBE_SCREEN_OPTION_PREV], event))
+ {
+ cubeLoadSvg (s, (cs->svgCurFile - 1 + cs->svgNFile) %
+ cs->svgNFile);
+ damageScreen (s);
+ }
+ }
+ default:
+ break;
+ }
+
+ UNWRAP (cd, d, handleEvent);
+ (*d->handleEvent) (d, event);
+ WRAP (cd, d, handleEvent, cubeHandleEvent);
+}
+#endif
+
+static Bool
+cubeSetGlobalScreenOption (CompScreen *s,
+ char *name,
+ CompOptionValue *value)
+{
+ Bool status;
+
+ CUBE_SCREEN (s);
+
+ UNWRAP (cs, s, setScreenOption);
+ status = (*s->setScreenOption) (s, name, value);
+ WRAP (cs, s, setScreenOption, cubeSetGlobalScreenOption);
+
+ if (status && strcmp (name, "size") == 0)
+ cubeUpdateGeometry (s, s->size, cs->invert);
+
+ return status;
+}
+
+static Bool
+cubeInitDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ CubeDisplay *cd;
+
+ cd = malloc (sizeof (CubeDisplay));
+ if (!cd)
+ return FALSE;
+
+ cd->screenPrivateIndex = allocateScreenPrivateIndex (d);
+ if (cd->screenPrivateIndex < 0)
+ {
+ free (cd);
+ return FALSE;
+ }
+
+#ifdef USE_LIBSVG_CAIRO
+ WRAP (cd, d, handleEvent, cubeHandleEvent);
+#endif
+
+ d->privates[displayPrivateIndex].ptr = cd;
+
+ return TRUE;
+}
+
+static void
+cubeFiniDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ CUBE_DISPLAY (d);
+
+ freeScreenPrivateIndex (d, cd->screenPrivateIndex);
+
+#ifdef USE_LIBSVG_CAIRO
+ UNWRAP (cd, d, handleEvent);
+#endif
+
+ free (cd);
+}
+
+static Bool
+cubeInitScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ CubeScreen *cs;
+
+ CUBE_DISPLAY (s->display);
+
+ cs = malloc (sizeof (CubeScreen));
+ if (!cs)
+ return FALSE;
+
+ 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;
+
+ cs->color[0] = CUBE_COLOR_RED_DEFAULT;
+ cs->color[1] = CUBE_COLOR_GREEN_DEFAULT;
+ cs->color[2] = CUBE_COLOR_BLUE_DEFAULT;
+
+ cs->nvertices = 0;
+ cs->vertices = NULL;
+
+ s->privates[cd->screenPrivateIndex].ptr = cs;
+
+ cs->paintTopBottom = FALSE;
+
+ initTexture (s, &cs->texture);
+
+#ifdef USE_LIBSVG_CAIRO
+ cubeInitSvg (s);
+
+ cs->svgFiles = 0;
+ cs->svgNFile = 0;
+ cs->svgCurFile = 0;
+#endif
+
+ cubeScreenInitOptions (cs, s->display->display);
+
+ WRAP (cs, s, paintTransformedScreen, cubePaintTransformedScreen);
+ WRAP (cs, s, paintBackground, cubePaintBackground);
+ WRAP (cs, s, setScreenOption, cubeSetGlobalScreenOption);
+
+ if (!cubeUpdateGeometry (s, s->size, cs->invert))
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+cubeFiniScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ CUBE_SCREEN (s);
+
+ UNWRAP (cs, s, paintTransformedScreen);
+ UNWRAP (cs, s, paintBackground);
+ UNWRAP (cs, s, setScreenOption);
+
+ finiTexture (s, &cs->texture);
+
+#ifdef USE_LIBSVG_CAIRO
+ cubeFiniSvg (s);
+#endif
+
+ free (cs);
+}
+
+static Bool
+cubeInit (CompPlugin *p)
+{
+ displayPrivateIndex = allocateDisplayPrivateIndex ();
+ if (displayPrivateIndex < 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+cubeFini (CompPlugin *p)
+{
+ if (displayPrivateIndex >= 0)
+ freeDisplayPrivateIndex (displayPrivateIndex);
+}
+
+CompPluginDep cubeDeps[] = {
+ { CompPluginRuleBefore, "expose" }
+};
+
+CompPluginVTable cubeVTable = {
+ "cube",
+ "Desktop Cube",
+ "Place windows on cube",
+ cubeInit,
+ cubeFini,
+ cubeInitDisplay,
+ cubeFiniDisplay,
+ cubeInitScreen,
+ cubeFiniScreen,
+ 0, /* InitWindow */
+ 0, /* FiniWindow */
+ 0, /* GetDisplayOptions */
+ 0, /* SetDisplayOption */
+ cubeGetScreenOptions,
+ cubeSetScreenOption,
+ cubeDeps,
+ sizeof (cubeDeps) / sizeof (cubeDeps[0])
+};
+
+CompPluginVTable *
+getCompPluginInfo (void)
+{
+ return &cubeVTable;
+}
diff --git a/plugins/decoration.c b/plugins/decoration.c
new file mode 100644
index 00000000..a0e2704c
--- /dev/null
+++ b/plugins/decoration.c
@@ -0,0 +1,1321 @@
+/*
+ * 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 <X11/Xatom.h>
+#include <X11/extensions/shape.h>
+
+#include <compiz.h>
+
+#define GRAVITY_WEST (0)
+#define GRAVITY_EAST (1 << 0)
+#define GRAVITY_NORTH (0)
+#define GRAVITY_SOUTH (1 << 1)
+
+#define ALIGN_LEFT (0)
+#define ALIGN_RIGHT (1 << 0)
+#define ALIGN_TOP (0)
+#define ALIGN_BOTTOM (1 << 1)
+
+#define XX_MASK (1 << 6)
+#define XY_MASK (1 << 7)
+#define YX_MASK (1 << 8)
+#define YY_MASK (1 << 9)
+
+typedef struct _Point {
+ int x;
+ int y;
+ int gravity;
+} Point;
+
+typedef struct _Vector {
+ int dx;
+ int dy;
+ int x0;
+ int y0;
+} Vector;
+
+typedef struct _Quad {
+ Point p1;
+ Point p2;
+ int maxWidth;
+ int maxHeight;
+ int align;
+ CompMatrix m;
+} Quad;
+
+#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;
+
+ XRectangle *damageRects;
+ int sizeDamage;
+ int nDamage;
+} DecorTexture;
+
+typedef struct _Decoration {
+ int refCount;
+ DecorTexture *texture;
+ CompWindowExtents output;
+ CompWindowExtents input;
+ Quad *quad;
+ int nQuad;
+} Decoration;
+
+typedef struct _ScaledQuad {
+ CompMatrix matrix;
+ BoxRec box;
+} ScaledQuad;
+
+typedef struct _WindowDecoration {
+ Decoration *decor;
+ ScaledQuad *quad;
+ int nQuad;
+} WindowDecoration;
+
+static int displayPrivateIndex;
+
+typedef struct _DecorDisplay {
+ int screenPrivateIndex;
+ HandleEventProc handleEvent;
+ DecorTexture *textures;
+ Atom supportingDmCheckAtom;
+ Atom winDecorAtom;
+ Atom winDecorBareAtom;
+ Atom winDecorNormalAtom;
+ Atom winDecorActiveAtom;
+ Atom winDecorSyncAtom;
+} DecorDisplay;
+
+typedef struct _DecorScreen {
+ int windowPrivateIndex;
+
+ Window dmWin;
+
+ Decoration *decor[DECOR_NUM];
+
+ PaintWindowProc paintWindow;
+ DamageWindowRectProc damageWindowRect;
+
+ WindowMoveNotifyProc windowMoveNotify;
+ WindowResizeNotifyProc windowResizeNotify;
+} DecorScreen;
+
+typedef struct _DecorWindow {
+ WindowDecoration *wd;
+ Decoration *decor;
+} DecorWindow;
+
+#define GET_DECOR_DISPLAY(d) \
+ ((DecorDisplay *) (d)->privates[displayPrivateIndex].ptr)
+
+#define DECOR_DISPLAY(d) \
+ DecorDisplay *dd = GET_DECOR_DISPLAY (d)
+
+#define GET_DECOR_SCREEN(s, dd) \
+ ((DecorScreen *) (s)->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)->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)))
+
+
+static Bool
+decorPaintWindow (CompWindow *w,
+ const WindowPaintAttrib *attrib,
+ Region region,
+ unsigned int mask)
+{
+ Bool status;
+
+ DECOR_SCREEN (w->screen);
+
+ if (!(mask & PAINT_WINDOW_SOLID_MASK))
+ {
+ DECOR_WINDOW (w);
+
+ if (dw->wd)
+ {
+ WindowDecoration *wd = dw->wd;
+ REGION box;
+ int i;
+
+ if (mask & PAINT_WINDOW_TRANSFORMED_MASK)
+ region = &infiniteRegion;
+
+ box.rects = &box.extents;
+ box.numRects = 1;
+
+ if (w->alpha && wd->decor == ds->decor[DECOR_BARE])
+ {
+ if (attrib->saturation == COLOR)
+ {
+ CompMatrix matrix[2];
+
+ if (!w->texture.pixmap)
+ bindWindow (w);
+
+ matrix[1] = w->texture.matrix;
+
+ w->vCount = 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)
+ {
+ matrix[0] = wd->quad[i].matrix;
+
+ (*w->screen->addWindowGeometry) (w,
+ matrix, 2,
+ &box,
+ region);
+ }
+ }
+
+ if (w->vCount)
+ {
+ int filter;
+
+ if ((mask & PAINT_WINDOW_TRANSFORMED_MASK) ||
+ (mask & PAINT_WINDOW_ON_TRANSFORMED_SCREEN_MASK))
+ filter = COMP_TEXTURE_FILTER_GOOD;
+ else
+ filter = COMP_TEXTURE_FILTER_FAST;
+
+ glEnable (GL_BLEND);
+
+ glPushMatrix ();
+
+ if (mask & PAINT_WINDOW_TRANSFORMED_MASK)
+ {
+ glTranslatef (w->attrib.x, w->attrib.y, 0.0f);
+ glScalef (attrib->xScale, attrib->yScale, 0.0f);
+ glTranslatef (-w->attrib.x, -w->attrib.y, 0.0f);
+ }
+
+ enableTexture (w->screen, &wd->decor->texture->texture,
+ filter);
+
+ if (attrib->opacity != OPAQUE ||
+ attrib->brightness != BRIGHT)
+ {
+ GLushort color;
+
+ color = (attrib->opacity * attrib->brightness) >> 16;
+
+ glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,
+ GL_MODULATE);
+ glColor4us (color, color, color, attrib->opacity);
+ }
+
+ w->screen->activeTexture (GL_TEXTURE1_ARB);
+
+ enableTexture (w->screen, &w->texture, filter);
+
+ glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,
+ GL_MODULATE);
+
+ w->screen->clientActiveTexture (GL_TEXTURE1_ARB);
+ glEnableClientState (GL_TEXTURE_COORD_ARRAY);
+ w->screen->clientActiveTexture (GL_TEXTURE0_ARB);
+
+ (*w->screen->drawWindowGeometry) (w);
+
+ disableTexture (&w->texture);
+ w->screen->activeTexture (GL_TEXTURE0_ARB);
+
+ w->screen->clientActiveTexture (GL_TEXTURE1_ARB);
+ glDisableClientState (GL_TEXTURE_COORD_ARRAY);
+ w->screen->clientActiveTexture (GL_TEXTURE0_ARB);
+
+ disableTexture (&wd->decor->texture->texture);
+
+ glPopMatrix ();
+
+ if (attrib->opacity != OPAQUE ||
+ attrib->brightness != BRIGHT)
+ {
+ glColor4usv (defaultColor);
+ glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,
+ GL_REPLACE);
+ }
+
+ glDisable (GL_BLEND);
+ }
+ }
+ }
+ else
+ {
+ w->vCount = 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)
+ drawWindowTexture (w, &wd->decor->texture->texture, attrib,
+ mask | PAINT_WINDOW_TRANSLUCENT_MASK);
+ }
+ }
+ }
+
+ UNWRAP (ds, w->screen, paintWindow);
+ status = (*w->screen->paintWindow) (w, attrib, region, mask);
+ WRAP (ds, w->screen, paintWindow, decorPaintWindow);
+
+ 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;
+ }
+
+ texture->damage = XDamageCreate (screen->display->display, pixmap,
+ XDamageReportRawRectangles);
+
+ texture->refCount = 1;
+ texture->pixmap = pixmap;
+ texture->next = dd->textures;
+
+ texture->damageRects = 0;
+ texture->sizeDamage = 0;
+ texture->nDamage = 0;
+
+ 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);
+}
+
+/*
+ decoration property
+ -------------------
+
+ data[0] = pixmap
+
+ data[1] = input left
+ data[2] = input right
+ data[3] = input top
+ data[4] = input bottom
+
+ flags
+
+ 1st and 2nd bit p1 gravity, 3rd and 4th bit p2 gravity,
+ 5rd and 6th bit alignment, 7th bit XX, 8th bit XY, 9th bit YX, 10th bit YY.
+
+ data[4 + n * 9 + 1] = flags
+ data[4 + n * 9 + 2] = p1 x
+ data[4 + n * 9 + 3] = p1 y
+ data[4 + n * 9 + 4] = p2 x
+ data[4 + n * 9 + 5] = p2 y
+ data[4 + n * 9 + 6] = widthMax
+ data[4 + n * 9 + 7] = heightMax
+ data[4 + n * 9 + 8] = x0
+ data[4 + n * 9 + 9] = y0
+ */
+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;
+ Quad *quad;
+ int nQuad;
+ int left, right, top, bottom;
+ int flags;
+
+ result = XGetWindowProperty (screen->display->display, id,
+ decorAtom, 0L, 1024L, FALSE,
+ XA_INTEGER, &actual, &format,
+ &n, &nleft, &data);
+
+ if (result != Success || !n || !data)
+ return NULL;
+
+ if (n < 9 + 14)
+ {
+ XFree (data);
+ return NULL;
+ }
+
+ decoration = malloc (sizeof (Decoration));
+ if (!decoration)
+ {
+ XFree (data);
+ return NULL;
+ }
+
+ prop = (long *) data;
+
+ memcpy (&pixmap, prop++, sizeof (Pixmap));
+
+ decoration->texture = decorGetTexture (screen, pixmap);
+ if (!decoration->texture)
+ {
+ free (decoration);
+ XFree (data);
+ return NULL;
+ }
+
+ decoration->input.left = *prop++;
+ decoration->input.right = *prop++;
+ decoration->input.top = *prop++;
+ decoration->input.bottom = *prop++;
+
+ nQuad = (n - 5) / 9;
+
+ quad = malloc (sizeof (Quad) * nQuad);
+ if (!quad)
+ {
+ decorReleaseTexture (screen, decoration->texture);
+ free (decoration);
+ XFree (data);
+ return NULL;
+ }
+
+ decoration->quad = quad;
+ decoration->nQuad = nQuad;
+
+ left = right = top = bottom = 0;
+
+ while (nQuad--)
+ {
+ flags = *prop++;
+
+ quad->p1.gravity = (flags >> 0) & 0x3;
+ quad->p2.gravity = (flags >> 2) & 0x3;
+
+ quad->align = (flags >> 4) & 0x3;
+
+ quad->m.xx = (flags & XX_MASK) ? 1.0f : 0.0f;
+ quad->m.xy = (flags & XY_MASK) ? 1.0f : 0.0f;
+ quad->m.yx = (flags & YX_MASK) ? 1.0f : 0.0f;
+ quad->m.yy = (flags & YY_MASK) ? 1.0f : 0.0f;
+
+ quad->p1.x = *prop++;
+ quad->p1.y = *prop++;
+ quad->p2.x = *prop++;
+ quad->p2.y = *prop++;
+
+ quad->maxWidth = *prop++;
+ quad->maxHeight = *prop++;
+
+ quad->m.x0 = *prop++;
+ quad->m.y0 = *prop++;
+
+ if (quad->p1.x < left)
+ left = quad->p1.x;
+ if (quad->p1.y < top)
+ top = quad->p1.y;
+ if (quad->p2.x > right)
+ right = quad->p2.x;
+ if (quad->p2.y > bottom)
+ bottom = quad->p2.y;
+
+ quad++;
+ }
+
+ XFree (data);
+
+ decoration->output.left = -left;
+ decoration->output.right = right;
+ decoration->output.top = -top;
+ decoration->output.bottom = 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);
+}
+
+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;
+
+ wd->decor = d;
+ wd->quad = (ScaledQuad *) (wd + 1);
+ wd->nQuad = d->nQuad;
+
+ return wd;
+}
+
+static void
+destroyWindowDecoration (WindowDecoration *wd)
+{
+ free (wd);
+}
+
+static void
+setDecorationMatrices (CompWindow *w)
+{
+ WindowDecoration *wd;
+ int i;
+
+ 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;
+
+ wd->quad[i].matrix.x0 += wd->decor->quad[i].m.x0 * wd->quad[i].matrix.xx;
+ wd->quad[i].matrix.y0 += wd->decor->quad[i].m.y0 * wd->quad[i].matrix.yy;
+
+ wd->quad[i].matrix.xx *= wd->decor->quad[i].m.xx;
+ wd->quad[i].matrix.yy *= wd->decor->quad[i].m.yy;
+ wd->quad[i].matrix.xy *= wd->decor->quad[i].m.xy;
+ wd->quad[i].matrix.yx *= wd->decor->quad[i].m.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
+applyGravity (int gravity,
+ int x,
+ int y,
+ int width,
+ int height,
+ int *return_x,
+ int *return_y)
+{
+ if (gravity & GRAVITY_EAST)
+ *return_x = x + width;
+ else
+ *return_x = x;
+
+ if (gravity & GRAVITY_SOUTH)
+ *return_y = y + height;
+ else
+ *return_y = y;
+}
+
+static void
+updateWindowDecorationScale (CompWindow *w)
+{
+ WindowDecoration *wd;
+ int x1, y1, x2, y2;
+ int maxWidth, maxHeight;
+ int align;
+ int i;
+
+ DECOR_WINDOW (w);
+
+ wd = dw->wd;
+ if (!wd)
+ return;
+
+ for (i = 0; i < wd->nQuad; i++)
+ {
+ applyGravity (wd->decor->quad[i].p1.gravity,
+ wd->decor->quad[i].p1.x, wd->decor->quad[i].p1.y,
+ w->width, w->height,
+ &x1, &y1);
+
+ applyGravity (wd->decor->quad[i].p2.gravity,
+ wd->decor->quad[i].p2.x, wd->decor->quad[i].p2.y,
+ w->width, w->height,
+ &x2, &y2);
+
+ maxWidth = wd->decor->quad[i].maxWidth;
+ maxHeight = wd->decor->quad[i].maxHeight;
+ align = wd->decor->quad[i].align;
+
+ if (maxWidth < x2 - x1)
+ {
+ if (align & ALIGN_RIGHT)
+ x1 = x2 - maxWidth;
+ else
+ x2 = x1 + maxWidth;
+ }
+
+ if (maxHeight < y2 - y1)
+ {
+ if (align & ALIGN_BOTTOM)
+ y1 = y2 - maxHeight;
+ else
+ y2 = y1 + maxHeight;
+ }
+
+ 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;
+ }
+
+ setDecorationMatrices (w);
+}
+
+static Bool
+decorWindowUpdate (CompWindow *w,
+ Bool move)
+{
+ WindowDecoration *wd;
+ Decoration *old, *decor = NULL;
+ int dx, dy;
+
+ DECOR_SCREEN (w->screen);
+ DECOR_WINDOW (w);
+
+ wd = dw->wd;
+ old = (wd) ? wd->decor : NULL;
+
+ if (dw->decor)
+ {
+ decor = dw->decor;
+ }
+ else
+ {
+ if (w->attrib.override_redirect)
+ {
+ if (w->region->numRects == 1)
+ decor = ds->decor[DECOR_BARE];
+ }
+ else
+ {
+ switch (w->type) {
+ case CompWindowTypeDialogMask:
+ case CompWindowTypeModalDialogMask:
+ case CompWindowTypeUtilMask:
+ case CompWindowTypeNormalMask:
+ if (w->mwmDecor & MwmDecorAll)
+ {
+ if (w->id == w->screen->display->activeWindow)
+ decor = ds->decor[DECOR_ACTIVE];
+ else
+ decor = ds->decor[DECOR_NORMAL];
+
+ break;
+ }
+ /* fall-through */
+ case CompWindowTypeSplashMask:
+ case CompWindowTypeToolbarMask:
+ case CompWindowTypeMenuMask:
+ case CompWindowTypeUnknownMask:
+ case CompWindowTypeDockMask:
+ if (w->region->numRects == 1)
+ decor = ds->decor[DECOR_BARE];
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ if (!ds->dmWin)
+ decor = NULL;
+
+ if (decor == old)
+ return FALSE;
+
+ if (old)
+ {
+ damageWindowOutputExtents (w);
+
+ if (wd->decor == dw->decor)
+ {
+ decorReleaseDecoration (w->screen, dw->decor);
+ dw->decor = NULL;
+ }
+
+ destroyWindowDecoration (wd);
+ }
+
+ if (decor)
+ {
+ dx = decor->input.left;
+ dy = decor->input.top;
+ }
+ else
+ dx = dy = 0;
+
+ dx -= w->input.left;
+ dy -= w->input.top;
+
+ /* if (dx == 0 && dy == 0) */
+ move = FALSE;
+
+ if (decor)
+ {
+ dw->wd = createWindowDecoration (decor);
+ if (!dw->wd)
+ return FALSE;
+
+ setWindowFrameExtents (w, &decor->input, &decor->output);
+
+ if (!move)
+ damageWindowOutputExtents (w);
+
+ updateWindowDecorationScale (w);
+ }
+ else
+ {
+ dw->wd = NULL;
+ }
+
+ if (move)
+ moveWindow (w, dx, dy, TRUE);
+
+ 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 && n && data)
+ {
+ XWindowAttributes attr;
+
+ memcpy (&dmWin, data, sizeof (Window));
+ XFree (data);
+
+ compCheckForError (d->display);
+
+ XGetWindowAttributes (d->display, dmWin, &attr);
+
+ if (compCheckForError (d->display))
+ dmWin = None;
+ }
+
+ if (dmWin != ds->dmWin)
+ {
+ CompWindow *w;
+
+ if (dmWin)
+ {
+ ds->decor[DECOR_BARE] =
+ decorCreateDecoration (s, s->root, dd->winDecorBareAtom);
+
+ ds->decor[DECOR_NORMAL] =
+ decorCreateDecoration (s, s->root, dd->winDecorNormalAtom);
+
+ ds->decor[DECOR_ACTIVE] =
+ decorCreateDecoration (s, s->root, dd->winDecorActiveAtom);
+ }
+ else
+ {
+ int i;
+
+ for (i = 0; i < DECOR_NUM; i++)
+ {
+ if (ds->decor[i])
+ {
+ decorReleaseDecoration (s, ds->decor[i]);
+ ds->decor[i] = 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 = 0;
+ CompWindow *w;
+
+ DECOR_DISPLAY (d);
+
+ switch (event->type) {
+ case PropertyNotify:
+ if (event->xproperty.atom == d->winActiveAtom)
+ activeWindow = d->activeWindow;
+ break;
+ case DestroyNotify:
+ w = findWindowAtDisplay (d, event->xdestroywindow.window);
+ if (w)
+ {
+ DECOR_SCREEN (w->screen);
+
+ if (w->id == ds->dmWin)
+ decorCheckForDmOnScreen (w->screen, TRUE);
+ }
+ 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)
+ {
+ if (t->nDamage == t->sizeDamage)
+ {
+ if (t->damageRects)
+ {
+ t->damageRects = realloc (t->damageRects,
+ (t->sizeDamage + 1) *
+ sizeof (XRectangle));
+ t->sizeDamage += 1;
+ }
+ else
+ {
+ t->damageRects = malloc (sizeof (XRectangle));
+ t->sizeDamage = 1;
+ }
+ }
+
+ t->damageRects[t->nDamage].x = de->area.x;
+ t->damageRects[t->nDamage].y = de->area.y;
+ t->damageRects[t->nDamage].width = de->area.width;
+ t->damageRects[t->nDamage].height = de->area.height;
+ t->nDamage++;
+
+ return;
+ }
+ }
+ }
+ break;
+ }
+
+ UNWRAP (dd, d, handleEvent);
+ (*d->handleEvent) (d, event);
+ WRAP (dd, d, handleEvent, decorHandleEvent);
+
+ switch (event->type) {
+ case PropertyNotify:
+ if (event->xproperty.atom == d->winActiveAtom)
+ {
+ if (d->activeWindow != activeWindow)
+ {
+ w = findWindowAtDisplay (d, activeWindow);
+ if (w)
+ decorWindowUpdate (w, FALSE);
+
+ w = findWindowAtDisplay (d, d->activeWindow);
+ if (w)
+ decorWindowUpdate (w, FALSE);
+ }
+ }
+ else 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 if (event->xproperty.atom == dd->supportingDmCheckAtom)
+ {
+ CompScreen *s;
+
+ s = findScreenAtDisplay (d, event->xproperty.window);
+ if (s)
+ decorCheckForDmOnScreen (s, TRUE);
+ }
+ break;
+ case ClientMessage:
+ if (event->xclient.message_type == dd->winDecorSyncAtom)
+ {
+ DecorTexture *t;
+
+ for (t = dd->textures; t; t = t->next)
+ if (t->pixmap == event->xclient.data.l[0])
+ break;
+
+ if (t)
+ {
+ DecorWindow *dw;
+ DecorScreen *ds;
+ CompScreen *s;
+
+ if (!t->nDamage)
+ return;
+
+ for (s = d->screens; s; s = s->next)
+ {
+ ds = GET_DECOR_SCREEN (s, dd);
+
+ for (w = s->windows; w; w = w->next)
+ {
+ if (!w->attrib.override_redirect && w->mapNum)
+ {
+ dw = GET_DECOR_WINDOW (w, ds);
+
+ if (dw->wd && dw->wd->decor->texture == t)
+ damageWindowOutputExtents (w);
+ }
+ }
+ }
+ }
+ }
+ break;
+ case MapRequest:
+ w = findWindowAtDisplay (d, event->xmaprequest.window);
+ if (w)
+ 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, FALSE);
+
+ UNWRAP (ds, w->screen, damageWindowRect);
+ status = (*w->screen->damageWindowRect) (w, initial, rect);
+ WRAP (ds, w->screen, damageWindowRect, decorDamageWindowRect);
+
+ return status;
+}
+
+static void
+decorWindowMoveNotify (CompWindow *w,
+ int dx,
+ int dy)
+{
+ 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);
+ WRAP (ds, w->screen, windowMoveNotify, decorWindowMoveNotify);
+}
+
+static void
+decorWindowResizeNotify (CompWindow *w)
+{
+ DECOR_SCREEN (w->screen);
+
+ if (!decorWindowUpdate (w, FALSE))
+ updateWindowDecorationScale (w);
+
+ UNWRAP (ds, w->screen, windowResizeNotify);
+ (*w->screen->windowResizeNotify) (w);
+ WRAP (ds, w->screen, windowResizeNotify, decorWindowResizeNotify);
+}
+
+static Bool
+decorInitDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ DecorDisplay *dd;
+
+ dd = malloc (sizeof (DecorDisplay));
+ if (!dd)
+ return FALSE;
+
+ dd->screenPrivateIndex = allocateScreenPrivateIndex (d);
+ if (dd->screenPrivateIndex < 0)
+ {
+ free (dd);
+ return FALSE;
+ }
+
+ dd->textures = 0;
+
+ dd->supportingDmCheckAtom =
+ XInternAtom (d->display, "_NET_SUPPORTING_DM_CHECK", 0);
+ dd->winDecorAtom = XInternAtom (d->display, "_NET_WINDOW_DECOR", 0);
+ dd->winDecorBareAtom =
+ XInternAtom (d->display, "_NET_WINDOW_DECOR_BARE", 0);
+ dd->winDecorNormalAtom =
+ XInternAtom (d->display, "_NET_WINDOW_DECOR_NORMAL", 0);
+ dd->winDecorActiveAtom =
+ XInternAtom (d->display, "_NET_WINDOW_DECOR_ACTIVE", 0);
+ dd->winDecorSyncAtom =
+ XInternAtom (d->display, "_NET_WINDOW_DECOR_SYNC", 0);
+
+ WRAP (dd, d, handleEvent, decorHandleEvent);
+
+ d->privates[displayPrivateIndex].ptr = dd;
+
+ return TRUE;
+}
+
+static void
+decorFiniDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ DECOR_DISPLAY (d);
+
+ freeScreenPrivateIndex (d, dd->screenPrivateIndex);
+
+ UNWRAP (dd, d, handleEvent);
+
+ 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;
+
+ WRAP (ds, s, paintWindow, decorPaintWindow);
+ WRAP (ds, s, damageWindowRect, decorDamageWindowRect);
+ WRAP (ds, s, windowMoveNotify, decorWindowMoveNotify);
+ WRAP (ds, s, windowResizeNotify, decorWindowResizeNotify);
+
+ s->privates[dd->screenPrivateIndex].ptr = ds;
+
+ decorCheckForDmOnScreen (s, FALSE);
+
+ 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]);
+
+ UNWRAP (ds, s, paintWindow);
+ UNWRAP (ds, s, damageWindowRect);
+ UNWRAP (ds, s, windowMoveNotify);
+ UNWRAP (ds, s, windowResizeNotify);
+
+ 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;
+
+ w->privates[ds->windowPrivateIndex].ptr = dw;
+
+ if (w->attrib.map_state == IsViewable)
+ {
+ if (!w->attrib.override_redirect)
+ decorWindowUpdateDecoration (w);
+
+ decorWindowUpdate (w, FALSE);
+ }
+
+ return TRUE;
+}
+
+static void
+decorFiniWindow (CompPlugin *p,
+ CompWindow *w)
+{
+ DECOR_WINDOW (w);
+
+ if (dw->wd)
+ destroyWindowDecoration (dw->wd);
+
+ if (dw->decor)
+ decorReleaseDecoration (w->screen, dw->decor);
+
+ free (dw);
+}
+
+static Bool
+decorInit (CompPlugin *p)
+{
+ displayPrivateIndex = allocateDisplayPrivateIndex ();
+ if (displayPrivateIndex < 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+decorFini (CompPlugin *p)
+{
+ if (displayPrivateIndex >= 0)
+ freeDisplayPrivateIndex (displayPrivateIndex);
+}
+
+CompPluginDep decorDeps[] = {
+ { CompPluginRuleBefore, "wobbly" },
+ { CompPluginRuleBefore, "fade" },
+ { CompPluginRuleBefore, "cube" },
+ { CompPluginRuleBefore, "expose" }
+};
+
+static CompPluginVTable decorVTable = {
+ "decoration",
+ "Window Decoration",
+ "Window decorations",
+ decorInit,
+ decorFini,
+ decorInitDisplay,
+ decorFiniDisplay,
+ decorInitScreen,
+ decorFiniScreen,
+ decorInitWindow,
+ decorFiniWindow,
+ 0, /* GetDisplayOptions */
+ 0, /* SetDisplayOption */
+ 0, /* GetScreenOptions */
+ 0, /* SetScreenOption */
+ decorDeps,
+ sizeof (decorDeps) / sizeof (decorDeps[0])
+};
+
+CompPluginVTable *
+getCompPluginInfo (void)
+{
+ return &decorVTable;
+}
diff --git a/plugins/fade.c b/plugins/fade.c
new file mode 100644
index 00000000..6162acbb
--- /dev/null
+++ b/plugins/fade.c
@@ -0,0 +1,720 @@
+/*
+ * 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.h>
+
+#define FADE_SPEED_DEFAULT 5.0f
+#define FADE_SPEED_MIN 0.1f
+#define FADE_SPEED_MAX 10.0f
+#define FADE_SPEED_PRECISION 0.1f
+
+static char *winType[] = {
+ "Dock",
+ "Toolbar",
+ "Menu",
+ "Utility",
+ "Splash",
+ "Normal",
+ "Dialog",
+ "ModalDialog",
+ "Unknown"
+};
+#define N_WIN_TYPE (sizeof (winType) / sizeof (winType[0]))
+
+static int displayPrivateIndex;
+
+typedef struct _FadeDisplay {
+ int screenPrivateIndex;
+ HandleEventProc handleEvent;
+ int displayModals;
+} FadeDisplay;
+
+#define FADE_SCREEN_OPTION_FADE_SPEED 0
+#define FADE_SCREEN_OPTION_WINDOW_TYPE 1
+#define FADE_SCREEN_OPTION_NUM 2
+
+typedef struct _FadeScreen {
+ int windowPrivateIndex;
+ int fadeTime;
+ int steps;
+
+ CompOption opt[FADE_SCREEN_OPTION_NUM];
+
+ PreparePaintScreenProc preparePaintScreen;
+ PaintWindowProc paintWindow;
+ DamageWindowRectProc damageWindowRect;
+ FocusWindowProc focusWindow;
+
+ int wMask;
+} FadeScreen;
+
+typedef struct _FadeWindow {
+ GLushort opacity;
+ GLushort brightness;
+ GLushort saturation;
+
+ int dModal;
+
+ int destroyCnt;
+ int unmapCnt;
+} FadeWindow;
+
+#define GET_FADE_DISPLAY(d) \
+ ((FadeDisplay *) (d)->privates[displayPrivateIndex].ptr)
+
+#define FADE_DISPLAY(d) \
+ FadeDisplay *fd = GET_FADE_DISPLAY (d)
+
+#define GET_FADE_SCREEN(s, fd) \
+ ((FadeScreen *) (s)->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)->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 CompOption *
+fadeGetScreenOptions (CompScreen *screen,
+ int *count)
+{
+ FADE_SCREEN (screen);
+
+ *count = NUM_OPTIONS (fs);
+ return fs->opt;
+}
+
+static Bool
+fadeSetScreenOption (CompScreen *screen,
+ 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_TYPE:
+ if (compSetOptionList (o, value))
+ {
+ fs->wMask = compWindowTypeMaskFromStringList (&o->value);
+ fs->wMask &= ~CompWindowTypeDesktopMask;
+ return TRUE;
+ }
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+static void
+fadeScreenInitOptions (FadeScreen *fs)
+{
+ CompOption *o;
+ int i;
+
+ o = &fs->opt[FADE_SCREEN_OPTION_FADE_SPEED];
+ o->name = "fade_speed";
+ o->shortDesc = "Fade Speed";
+ o->longDesc = "Window fade speed";
+ o->type = CompOptionTypeFloat;
+ o->value.f = FADE_SPEED_DEFAULT;
+ o->rest.f.min = FADE_SPEED_MIN;
+ o->rest.f.max = FADE_SPEED_MAX;
+ o->rest.f.precision = FADE_SPEED_PRECISION;
+
+ o = &fs->opt[FADE_SCREEN_OPTION_WINDOW_TYPE];
+ o->name = "window_types";
+ o->shortDesc = "Window Types";
+ o->longDesc = "Window types that should be fading";
+ o->type = CompOptionTypeList;
+ o->value.list.type = CompOptionTypeString;
+ o->value.list.nValue = N_WIN_TYPE;
+ o->value.list.value = malloc (sizeof (CompOptionValue) * N_WIN_TYPE);
+ for (i = 0; i < N_WIN_TYPE; i++)
+ o->value.list.value[i].s = strdup (winType[i]);
+ o->rest.s.string = windowTypeString;
+ o->rest.s.nString = nWindowTypeString;
+
+ fs->wMask = compWindowTypeMaskFromStringList (&o->value);
+}
+
+static void
+fadePreparePaintScreen (CompScreen *s,
+ int msSinceLastPaint)
+{
+ FADE_SCREEN (s);
+
+ fs->steps = (msSinceLastPaint * OPAQUE) / fs->fadeTime;
+ if (fs->steps < 12)
+ fs->steps = 12;
+
+ UNWRAP (fs, s, preparePaintScreen);
+ (*s->preparePaintScreen) (s, msSinceLastPaint);
+ WRAP (fs, s, preparePaintScreen, fadePreparePaintScreen);
+}
+
+static Bool
+fadePaintWindow (CompWindow *w,
+ const WindowPaintAttrib *attrib,
+ Region region,
+ unsigned int mask)
+{
+ CompScreen *s = w->screen;
+ Bool status;
+
+ FADE_SCREEN (s);
+ FADE_WINDOW (w);
+
+ if (!w->screen->canDoSlightlySaturated)
+ fw->saturation = attrib->saturation;
+
+ if (fw->opacity != attrib->opacity ||
+ fw->brightness != attrib->brightness ||
+ fw->saturation != attrib->saturation)
+ {
+ GLint opacity;
+ GLint brightness;
+ GLint saturation;
+
+ opacity = fw->opacity;
+ if (attrib->opacity > fw->opacity)
+ {
+ opacity = fw->opacity + fs->steps;
+ if (opacity > attrib->opacity)
+ opacity = attrib->opacity;
+ }
+ else if (attrib->opacity < fw->opacity)
+ {
+ if (w->type & CompWindowTypeUnknownMask)
+ opacity = fw->opacity - (fs->steps >> 1);
+ else
+ opacity = fw->opacity - fs->steps;
+
+ if (opacity < attrib->opacity)
+ opacity = attrib->opacity;
+ }
+
+ brightness = fw->brightness;
+ if (attrib->brightness > fw->brightness)
+ {
+ brightness = fw->brightness + fs->steps;
+ if (brightness > attrib->brightness)
+ brightness = attrib->brightness;
+ }
+ else if (attrib->brightness < fw->brightness)
+ {
+ brightness = fw->brightness - fs->steps;
+ if (brightness < attrib->brightness)
+ brightness = attrib->brightness;
+ }
+
+ saturation = fw->saturation;
+ if (attrib->saturation > fw->saturation)
+ {
+ saturation = fw->saturation + (fs->steps / 6);
+ if (saturation > attrib->saturation)
+ saturation = attrib->saturation;
+ }
+ else if (attrib->saturation < fw->saturation)
+ {
+ saturation = fw->saturation - (fs->steps / 12);
+ if (saturation < attrib->saturation)
+ saturation = attrib->saturation;
+ }
+
+ if (opacity > 0)
+ {
+ WindowPaintAttrib fAttrib = *attrib;
+
+ fAttrib.opacity = opacity;
+ fAttrib.brightness = brightness;
+ fAttrib.saturation = saturation;
+
+ UNWRAP (fs, s, paintWindow);
+ status = (*s->paintWindow) (w, &fAttrib, region, mask);
+ WRAP (fs, s, paintWindow, fadePaintWindow);
+
+ if (status)
+ {
+ fw->opacity = opacity;
+ fw->brightness = brightness;
+ fw->saturation = saturation;
+
+ if (opacity != attrib->opacity ||
+ brightness != attrib->brightness ||
+ saturation != attrib->saturation)
+ addWindowDamage (w);
+ }
+ }
+ else
+ {
+ fw->opacity = 0;
+
+ while (fw->unmapCnt)
+ {
+ unmapWindow (w);
+ fw->unmapCnt--;
+ }
+
+ while (fw->destroyCnt)
+ {
+ destroyWindow (w);
+ fw->destroyCnt--;
+ }
+
+ return (mask & PAINT_WINDOW_SOLID_MASK) ? FALSE : TRUE;
+ }
+ }
+ else
+ {
+ UNWRAP (fs, s, paintWindow);
+ status = (*s->paintWindow) (w, attrib, 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)
+ {
+ for (w = s->windows; w; w = w->next)
+ {
+ FADE_WINDOW (w);
+
+ if (fw->dModal)
+ continue;
+
+ w->paint.saturation = 0;
+ }
+
+ 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)
+ {
+ for (w = s->windows; w; w = w->next)
+ {
+ FADE_WINDOW (w);
+
+ if (fw->dModal)
+ continue;
+
+ if (w->alive)
+ w->paint.saturation = w->saturation;
+ }
+
+ damageScreen (s);
+ }
+ }
+}
+
+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 && (fs->wMask & w->type))
+ {
+ FADE_WINDOW (w);
+
+ w->paint.opacity = 0;
+
+ fw->destroyCnt++;
+ w->destroyRefCnt++;
+
+ addWindowDamage (w);
+ }
+
+ fadeRemoveDisplayModal (d, w);
+ }
+ break;
+ case UnmapNotify:
+ w = findWindowAtDisplay (d, event->xunmap.window);
+ if (w)
+ {
+ FADE_SCREEN (w->screen);
+
+ if (w->texture.pixmap && (fs->wMask & w->type))
+ {
+ FADE_WINDOW (w);
+
+ w->paint.opacity = 0;
+
+ fw->unmapCnt++;
+ w->unmapRefCnt++;
+
+ addWindowDamage (w);
+ }
+
+ fadeRemoveDisplayModal (d, w);
+ }
+ break;
+ case MapNotify:
+ w = findWindowAtDisplay (d, event->xunmap.window);
+ if (w)
+ {
+ FADE_WINDOW (w);
+
+ if (!(w->type & CompWindowTypeDesktopMask))
+ w->paint.opacity = getWindowProp32 (d, w->id,
+ d->winOpacityAtom,
+ OPAQUE);
+
+ while (fw->unmapCnt)
+ {
+ unmapWindow (w);
+ fw->unmapCnt--;
+ }
+
+ if (w->state & CompWindowStateDisplayModalMask)
+ fadeAddDisplayModal (d, w);
+ }
+ default:
+ 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;
+ }
+}
+
+static Bool
+fadeDamageWindowRect (CompWindow *w,
+ Bool initial,
+ BoxPtr rect)
+{
+ Bool status;
+
+ FADE_SCREEN (w->screen);
+
+ if (initial)
+ {
+ FADE_WINDOW (w);
+
+ if (fs->wMask & w->type)
+ fw->opacity = 0;
+ else
+ fw->opacity = w->paint.opacity;
+ }
+
+ 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 Bool
+fadeInitDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ FadeDisplay *fd;
+
+ fd = malloc (sizeof (FadeDisplay));
+ if (!fd)
+ return FALSE;
+
+ fd->screenPrivateIndex = allocateScreenPrivateIndex (d);
+ if (fd->screenPrivateIndex < 0)
+ {
+ free (fd);
+ return FALSE;
+ }
+
+ fd->displayModals = 0;
+
+ WRAP (fd, d, handleEvent, fadeHandleEvent);
+
+ d->privates[displayPrivateIndex].ptr = fd;
+
+ return TRUE;
+}
+
+static void
+fadeFiniDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ FADE_DISPLAY (d);
+
+ freeScreenPrivateIndex (d, fd->screenPrivateIndex);
+
+ UNWRAP (fd, d, handleEvent);
+
+ free (fd);
+}
+
+static Bool
+fadeInitScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ FadeScreen *fs;
+
+ FADE_DISPLAY (s->display);
+
+ fs = malloc (sizeof (FadeScreen));
+ if (!fs)
+ return FALSE;
+
+ fs->windowPrivateIndex = allocateWindowPrivateIndex (s);
+ if (fs->windowPrivateIndex < 0)
+ {
+ free (fs);
+ return FALSE;
+ }
+
+ fs->steps = 0;
+ fs->fadeTime = 1000.0f / FADE_SPEED_DEFAULT;
+
+ fadeScreenInitOptions (fs);
+
+ WRAP (fs, s, preparePaintScreen, fadePreparePaintScreen);
+ WRAP (fs, s, paintWindow, fadePaintWindow);
+ WRAP (fs, s, damageWindowRect, fadeDamageWindowRect);
+ WRAP (fs, s, focusWindow, fadeFocusWindow);
+
+ s->privates[fd->screenPrivateIndex].ptr = fs;
+
+ return TRUE;
+}
+
+static void
+fadeFiniScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ FADE_SCREEN (s);
+
+ freeWindowPrivateIndex (s, fs->windowPrivateIndex);
+
+ UNWRAP (fs, s, preparePaintScreen);
+ UNWRAP (fs, s, paintWindow);
+ UNWRAP (fs, s, damageWindowRect);
+ UNWRAP (fs, s, focusWindow);
+
+ 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->dModal = 0;
+
+ fw->destroyCnt = 0;
+ fw->unmapCnt = 0;
+
+ w->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);
+
+ while (fw->unmapCnt--)
+ unmapWindow (w);
+
+ while (fw->destroyCnt--)
+ destroyWindow (w);
+
+ free (fw);
+}
+
+static Bool
+fadeInit (CompPlugin *p)
+{
+ displayPrivateIndex = allocateDisplayPrivateIndex ();
+ if (displayPrivateIndex < 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+fadeFini (CompPlugin *p)
+{
+ if (displayPrivateIndex >= 0)
+ freeDisplayPrivateIndex (displayPrivateIndex);
+}
+
+CompPluginDep fadeDeps[] = {
+ { CompPluginRuleBefore, "cube" },
+ { CompPluginRuleBefore, "expose" }
+};
+
+static CompPluginVTable fadeVTable = {
+ "fade",
+ "Fading Windows",
+ "Fade in windows when mapped and fade out windows when unmapped",
+ fadeInit,
+ fadeFini,
+ fadeInitDisplay,
+ fadeFiniDisplay,
+ fadeInitScreen,
+ fadeFiniScreen,
+ fadeInitWindow,
+ fadeFiniWindow,
+ 0, /* GetDisplayOptions */
+ 0, /* SetDisplayOption */
+ fadeGetScreenOptions,
+ fadeSetScreenOption,
+ fadeDeps,
+ sizeof (fadeDeps) / sizeof (fadeDeps[0])
+};
+
+CompPluginVTable *
+getCompPluginInfo (void)
+{
+ return &fadeVTable;
+}
diff --git a/plugins/gconf.c b/plugins/gconf.c
new file mode 100644
index 00000000..0e8083c9
--- /dev/null
+++ b/plugins/gconf.c
@@ -0,0 +1,1036 @@
+/*
+ * 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.h>
+
+#define APP_NAME "/apps/compiz"
+#define KEY_CHANGE_TIMEOUT 250
+
+struct _GConfModifier {
+ char *name;
+ int modifier;
+} modifiers[] = {
+ { "<Shift>", ShiftMask },
+ { "<Control>", ControlMask },
+ { "<Mod1>", Mod1Mask },
+ { "<Mod2>", Mod2Mask },
+ { "<Mod3>", Mod3Mask },
+ { "<Mod4>", Mod4Mask },
+ { "<Mod5>", Mod5Mask },
+ { "<Alt>", CompAltMask },
+ { "<Meta>", CompMetaMask },
+ { "<Super>", CompSuperMask },
+ { "<Hyper>", CompHyperMask },
+ { "<ModeSwitch>", CompModeSwitchMask },
+ { "<Release>", CompReleaseMask }
+};
+
+#define N_MODIFIERS (sizeof (modifiers) / sizeof (struct _GConfModifier))
+
+static int displayPrivateIndex;
+
+typedef struct _GConfDisplay {
+ int screenPrivateIndex;
+
+ GConfClient *client;
+ CompTimeoutHandle timeoutHandle;
+
+ InitPluginForDisplayProc initPluginForDisplay;
+ SetDisplayOptionProc setDisplayOption;
+ SetDisplayOptionForPluginProc setDisplayOptionForPlugin;
+} GConfDisplay;
+
+typedef struct _GConfScreen {
+ InitPluginForScreenProc initPluginForScreen;
+ SetScreenOptionProc setScreenOption;
+ SetScreenOptionForPluginProc setScreenOptionForPlugin;
+} GConfScreen;
+
+#define GET_GCONF_DISPLAY(d) \
+ ((GConfDisplay *) (d)->privates[displayPrivateIndex].ptr)
+
+#define GCONF_DISPLAY(d) \
+ GConfDisplay *gd = GET_GCONF_DISPLAY (d)
+
+#define GET_GCONF_SCREEN(s, gd) \
+ ((GConfScreen *) (s)->privates[(gd)->screenPrivateIndex].ptr)
+
+#define GCONF_SCREEN(s) \
+ GConfScreen *gs = GET_GCONF_SCREEN (s, GET_GCONF_DISPLAY (s->display))
+
+static int
+strcmpskipifequal (char **ptr, char *s)
+{
+ int ret, len;
+
+ len = strlen (s);
+ ret = strncmp (*ptr, s, len);
+ if (ret == 0)
+ *ptr = (*ptr) + len;
+
+ return ret;
+}
+
+static GConfValueType
+gconfTypeFromCompType (CompOptionType type)
+{
+ switch (type) {
+ case CompOptionTypeBool:
+ return GCONF_VALUE_BOOL;
+ case CompOptionTypeInt:
+ return GCONF_VALUE_INT;
+ case CompOptionTypeFloat:
+ return GCONF_VALUE_FLOAT;
+ case CompOptionTypeString:
+ return GCONF_VALUE_STRING;
+ case CompOptionTypeColor:
+ return GCONF_VALUE_STRING;
+ case CompOptionTypeBinding:
+ return GCONF_VALUE_STRING;
+ case CompOptionTypeList:
+ return GCONF_VALUE_LIST;
+ default:
+ break;
+ }
+
+ return GCONF_VALUE_INVALID;
+}
+
+static void
+gconfSetValue (CompDisplay *d,
+ 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 = g_strdup_printf ("#%.2x%.2x%.2x",
+ value->c[0] / 256,
+ value->c[1] / 256,
+ value->c[2] / 256);
+
+ gconf_value_set_string (gvalue, color);
+
+ g_free (color);
+ } break;
+ case CompOptionTypeBinding: {
+ guint modMask;
+
+ if (value->bind.type == CompBindingTypeButton)
+ modMask = value->bind.u.button.modifiers;
+ else
+ modMask = value->bind.u.key.modifiers;
+
+ if (modMask & (CompPressMask | CompReleaseMask))
+ {
+ gchar *m, *mods = g_strdup ("");
+ gchar *binding;
+ gint i;
+
+ for (i = 0; i < N_MODIFIERS; i++)
+ {
+ if (modMask & modifiers[i].modifier)
+ {
+ m = g_strconcat (mods, modifiers[i].name, NULL);
+ if (m)
+ {
+ free (mods);
+ mods = m;
+ }
+ }
+ }
+
+ if (value->bind.type == CompBindingTypeButton)
+ {
+ binding = g_strdup_printf ("%sButton%d", mods,
+ value->bind.u.button.button);
+ }
+ else
+ {
+ KeySym keysym;
+ gchar *keyname;
+
+ keysym = XKeycodeToKeysym (d->display,
+ value->bind.u.key.keycode,
+ 0);
+ keyname = XKeysymToString (keysym);
+
+ if (keyname)
+ binding = g_strdup_printf ("%s%s", mods, keyname);
+ else
+ binding = g_strdup_printf ("%s0x%x", mods,
+ value->bind.u.key.keycode);
+
+ }
+
+ gconf_value_set_string (gvalue, binding);
+
+ g_free (binding);
+ g_free (mods);
+ }
+ else
+ {
+ gconf_value_set_string (gvalue, "Disabled");
+ }
+ } break;
+ default:
+ break;
+ }
+}
+
+static void
+gconfSetOption (CompDisplay *d,
+ CompOption *o,
+ gchar *screen,
+ gchar *plugin)
+{
+ GConfValue *gvalue;
+ gchar *key;
+
+ GCONF_DISPLAY (d);
+
+ if (plugin)
+ {
+ key = g_strjoin ("/", APP_NAME "/plugins", plugin, screen, "options",
+ o->name, NULL);
+ }
+ else
+ {
+ key = g_strjoin ("/", APP_NAME "/general", screen, "options", o->name,
+ NULL);
+ }
+
+ switch (o->type) {
+ case CompOptionTypeBool:
+ case CompOptionTypeInt:
+ case CompOptionTypeFloat:
+ case CompOptionTypeString:
+ case CompOptionTypeColor:
+ case CompOptionTypeBinding:
+ gvalue = gconf_value_new (gconfTypeFromCompType (o->type));
+ gconfSetValue (d, &o->value, o->type, gvalue);
+ gconf_client_set (gd->client, key, gvalue, NULL);
+ gconf_value_free (gvalue);
+ break;
+ case CompOptionTypeList: {
+ GConfValueType type;
+ GSList *list = NULL;
+ GConfValue *gv;
+ int i;
+
+ gvalue = gconf_value_new (GCONF_VALUE_LIST);
+
+ type = gconfTypeFromCompType (o->value.list.type);
+
+ for (i = 0; i < o->value.list.nValue; i++)
+ {
+ gv = gconf_value_new (type);
+ gconfSetValue (d, &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);
+ gconf_client_set (gd->client, key, gvalue, NULL);
+
+ for (i = 0; i < o->value.list.nValue; i++)
+ {
+ gv = g_slist_nth_data (list, i);
+ gconf_value_free (gv);
+ }
+ g_slist_free (list);
+ gconf_value_free (gvalue);
+ } break;
+ default:
+ break;
+ }
+
+ g_free (key);
+}
+
+static Bool
+gconfGetValue (CompDisplay *d,
+ 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)
+ {
+ value->s = (char *) gconf_value_get_string (gvalue);
+ return TRUE;
+ }
+ else if (type == CompOptionTypeColor &&
+ gvalue->type == GCONF_VALUE_STRING)
+ {
+ const gchar *color;
+ gint c[3];
+
+ color = gconf_value_get_string (gvalue);
+
+ if (sscanf (color, "#%2x%2x%2x", &c[0], &c[1], &c[2]) == 3)
+ {
+ value->c[0] = c[0] << 8 | c[0];
+ value->c[1] = c[1] << 8 | c[1];
+ value->c[2] = c[2] << 8 | c[2];
+ value->c[3] = 0xffff;
+
+ return TRUE;
+ }
+ }
+ else if (type == CompOptionTypeBinding &&
+ gvalue->type == GCONF_VALUE_STRING)
+ {
+ gchar *binding, *ptr;
+ gint i;
+ guint mods = 0;
+
+ binding = (gchar *) gconf_value_get_string (gvalue);
+ if (strcasecmp (binding, "disabled") == 0)
+ {
+ value->bind.type = CompBindingTypeButton;
+ value->bind.u.button.button = 1;
+ value->bind.u.button.modifiers = 0;
+ }
+ else
+ {
+ for (i = 0; i < N_MODIFIERS; i++)
+ {
+ if (strcasestr (binding, modifiers[i].name))
+ mods |= modifiers[i].modifier;
+ }
+
+ /* if not explicetly set to be triggered at release
+ assume it to be triggered at press */
+ if (!(mods & CompReleaseMask))
+ mods |= CompPressMask;
+
+ ptr = strrchr (binding, '>');
+ if (ptr)
+ binding = ptr + 1;
+
+ while (*binding && !isalnum (*binding))
+ binding++;
+
+ if (strcmpskipifequal (&binding, "Button") == 0)
+ {
+ gint button;
+
+ if (sscanf (binding, "%d", &button) == 1)
+ {
+ value->bind.type = CompBindingTypeButton;
+ value->bind.u.button.button = button;
+ value->bind.u.button.modifiers = mods;
+
+ return TRUE;
+ }
+ }
+ else
+ {
+ KeySym keysym;
+
+ keysym = XStringToKeysym (binding);
+ if (keysym != NoSymbol)
+ {
+ KeyCode keycode;
+
+ keycode = XKeysymToKeycode (d->display, keysym);
+ if (keycode)
+ {
+ value->bind.type = CompBindingTypeKey;
+ value->bind.u.key.keycode = keycode;
+ value->bind.u.key.modifiers = mods;
+
+ return TRUE;
+ }
+ }
+
+ if (strncmp (binding, "0x", 2) == 0)
+ {
+ value->bind.type = CompBindingTypeKey;
+ value->bind.u.key.keycode = strtol (binding, NULL, 0);
+ value->bind.u.key.modifiers = mods;
+
+ return TRUE;
+ }
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+static Bool
+gconfGetOptionValue (CompDisplay *d,
+ GConfEntry *entry)
+{
+ CompPlugin *p = 0;
+ CompScreen *s = 0;
+ CompOption *o, *option;
+ gchar *ptr;
+ gchar *pluginPtr = 0;
+ gint pluginLen = 0;
+ gint nOption;
+ Bool status = FALSE;
+
+ if (!entry)
+ return FALSE;
+
+ ptr = entry->key;
+ if (strncmp (ptr, APP_NAME, strlen (APP_NAME)))
+ return FALSE;
+
+ ptr += strlen (APP_NAME);
+
+ if (strcmpskipifequal (&ptr, "/plugins/") == 0)
+ {
+ pluginPtr = ptr;
+ ptr = strchr (ptr, '/');
+ if (!ptr)
+ return FALSE;
+
+ pluginLen = ptr - pluginPtr;
+ if (pluginLen < 1)
+ return FALSE;
+ }
+ else if (strcmpskipifequal (&ptr, "/general"))
+ return FALSE;
+
+ if (strcmpskipifequal (&ptr, "/screen") == 0)
+ {
+ int screenNum;
+
+ screenNum = strtol (ptr, &ptr, 0);
+
+ for (s = d->screens; s; s = s->next)
+ if (s->screenNum == screenNum)
+ break;
+
+ if (!s || !ptr)
+ return FALSE;
+ }
+ else if (strcmpskipifequal (&ptr, "/allscreens"))
+ return FALSE;
+
+ if (strcmpskipifequal (&ptr, "/options/"))
+ return FALSE;
+
+ if (pluginPtr)
+ {
+ pluginPtr = g_strndup (pluginPtr, pluginLen);
+
+ option = 0;
+ nOption = 0;
+
+ p = findActivePlugin (pluginPtr);
+ if (p)
+ {
+ if (s)
+ {
+ if (p->vTable->getScreenOptions)
+ option = (*p->vTable->getScreenOptions) (s, &nOption);
+ }
+ else
+ {
+ if (p->vTable->getDisplayOptions)
+ option = (*p->vTable->getDisplayOptions) (d, &nOption);
+ }
+ }
+ }
+ else
+ {
+ if (s)
+ option = compGetScreenOptions (s, &nOption);
+ else
+ option = compGetDisplayOptions (d, &nOption);
+ }
+
+ o = compFindOption (option, nOption, ptr, 0);
+ if (o)
+ {
+ GConfValue *gvalue;
+ CompOptionValue value;
+
+ gvalue = gconf_entry_get_value (entry);
+ if (gvalue)
+ {
+ if (o->type == CompOptionTypeList &&
+ gvalue->type == GCONF_VALUE_LIST)
+ {
+ GConfValueType type;
+
+ value.list.value = 0;
+ value.list.nValue = 0;
+
+ type = gconf_value_get_list_type (gvalue);
+ if (type == gconfTypeFromCompType (o->value.list.type))
+ {
+ GSList *list;
+ int i, length;
+
+ status = TRUE;
+
+ list = gconf_value_get_list (gvalue);
+
+ length = g_slist_length (list);
+
+ if (length)
+ {
+ value.list.value =
+ malloc (sizeof (CompOptionValue) * length);
+ if (value.list.value)
+ {
+ value.list.nValue = length;
+ for (i = 0; i < length; i++)
+ {
+ if (!gconfGetValue (d, &value.list.value[i],
+ o->value.list.type,
+ (GConfValue *) list->data))
+ status = FALSE;
+
+ list = g_slist_next (list);
+ }
+ }
+ else
+ status = FALSE;
+ }
+ }
+ else
+ status = FALSE;
+ }
+ else
+ {
+ status = gconfGetValue (d, &value, o->type, gvalue);
+ }
+
+ if (status)
+ {
+ if (s)
+ {
+ if (pluginPtr)
+ status = (*s->setScreenOptionForPlugin) (s,
+ pluginPtr,
+ ptr,
+ &value);
+ else
+ status = (*s->setScreenOption) (s, ptr, &value);
+ }
+ else
+ {
+ if (pluginPtr)
+ status = (*d->setDisplayOptionForPlugin) (d,
+ pluginPtr,
+ ptr,
+ &value);
+ else
+ status = (*d->setDisplayOption) (d, ptr, &value);
+ }
+ }
+
+ if (o->type == CompOptionTypeList &&
+ gvalue->type == GCONF_VALUE_LIST)
+ {
+ if (value.list.nValue && value.list.value)
+ free (value.list.value);
+ }
+ }
+
+ if (!status)
+ {
+ gchar *screen = 0;
+
+ if (s)
+ screen = g_strdup_printf ("screen%d", s->screenNum);
+
+ gconfSetOption (d, o, screen, pluginPtr);
+
+ if (screen)
+ g_free (screen);
+ }
+ }
+
+ if (pluginPtr)
+ g_free (pluginPtr);
+
+ return status;
+}
+
+static void
+gconfInitOption (CompDisplay *d,
+ CompOption *o,
+ gchar *screen,
+ gchar *plugin)
+{
+ GConfEntry *entry;
+ gchar *key;
+
+ GCONF_DISPLAY (d);
+
+ if (plugin)
+ {
+ key = g_strjoin ("/", APP_NAME "/plugins", plugin, screen,
+ "options", o->name, NULL);
+ }
+ else
+ {
+ key = g_strjoin ("/", APP_NAME "/general", screen, "options",
+ o->name, NULL);
+ }
+
+ entry = gconf_client_get_entry (gd->client, key, NULL, FALSE, NULL);
+
+ if (!gconfGetOptionValue (d, entry))
+ gconfSetOption (d, o, screen, plugin);
+
+ if (entry)
+ gconf_entry_free (entry);
+
+ g_free (key);
+}
+
+static void
+gconfInitPlugin (CompDisplay *d,
+ CompPlugin *p)
+{
+ gchar *gconfpath, *key;
+
+ GCONF_DISPLAY (d);
+
+ gconfpath = g_strjoin ("/", APP_NAME "/plugins", p->vTable->name, NULL);
+
+ key = g_strconcat (gconfpath, "/short_description", NULL);
+ gconf_client_set_string (gd->client, key, p->vTable->shortDesc, NULL);
+ g_free (key);
+
+ key = g_strconcat (gconfpath, "/long_description", NULL);
+ gconf_client_set_string (gd->client, key, p->vTable->longDesc, NULL);
+ g_free (key);
+
+ g_free (gconfpath);
+}
+
+static Bool
+gconfSetDisplayOption (CompDisplay *d,
+ char *name,
+ CompOptionValue *value)
+{
+ Bool status;
+
+ GCONF_DISPLAY (d);
+
+ UNWRAP (gd, d, setDisplayOption);
+ status = (*d->setDisplayOption) (d, name, value);
+ WRAP (gd, d, setDisplayOption, gconfSetDisplayOption);
+
+ if (status)
+ {
+ CompOption *option;
+ int nOption;
+
+ option = compGetDisplayOptions (d, &nOption);
+ gconfSetOption (d, compFindOption (option, nOption, name, 0),
+ "allscreens", 0);
+ }
+
+ return status;
+}
+
+static Bool
+gconfSetDisplayOptionForPlugin (CompDisplay *d,
+ char *plugin,
+ char *name,
+ CompOptionValue *value)
+{
+ Bool status;
+
+ GCONF_DISPLAY (d);
+
+ UNWRAP (gd, d, setDisplayOptionForPlugin);
+ status = (*d->setDisplayOptionForPlugin) (d, plugin, name, value);
+ WRAP (gd, d, setDisplayOptionForPlugin, gconfSetDisplayOptionForPlugin);
+
+ if (status)
+ {
+ CompPlugin *p;
+
+ p = findActivePlugin (plugin);
+ if (p && p->vTable->getDisplayOptions)
+ {
+ CompOption *option;
+ int nOption;
+
+ option = (*p->vTable->getDisplayOptions) (d, &nOption);
+ gconfSetOption (d, compFindOption (option, nOption, name, 0),
+ "allscreens", plugin);
+ }
+ }
+
+ return status;
+}
+
+static Bool
+gconfSetScreenOption (CompScreen *s,
+ char *name,
+ CompOptionValue *value)
+{
+ Bool status;
+
+ GCONF_SCREEN (s);
+
+ UNWRAP (gs, s, setScreenOption);
+ status = (*s->setScreenOption) (s, name, value);
+ WRAP (gs, s, setScreenOption, gconfSetScreenOption);
+
+ if (status)
+ {
+ CompOption *option;
+ int nOption;
+ gchar *screen;
+
+ screen = g_strdup_printf ("screen%d", s->screenNum);
+
+ option = compGetScreenOptions (s, &nOption);
+ gconfSetOption (s->display, compFindOption (option, nOption, name, 0),
+ screen, 0);
+
+ g_free (screen);
+ }
+
+ return status;
+}
+
+static Bool
+gconfSetScreenOptionForPlugin (CompScreen *s,
+ char *plugin,
+ char *name,
+ CompOptionValue *value)
+{
+ Bool status;
+
+ GCONF_SCREEN (s);
+
+ UNWRAP (gs, s, setScreenOptionForPlugin);
+ status = (*s->setScreenOptionForPlugin) (s, plugin, name, value);
+ WRAP (gs, s, setScreenOptionForPlugin, gconfSetScreenOptionForPlugin);
+
+ if (status)
+ {
+ CompPlugin *p;
+
+ p = findActivePlugin (plugin);
+ if (p && p->vTable->getScreenOptions)
+ {
+ CompOption *option;
+ int nOption;
+ gchar *screen;
+
+ screen = g_strdup_printf ("screen%d", s->screenNum);
+
+ option = (*p->vTable->getScreenOptions) (s, &nOption);
+ gconfSetOption (s->display,
+ compFindOption (option, nOption, name, 0),
+ screen, plugin);
+
+ g_free (screen);
+ }
+ }
+
+ return status;
+}
+
+static Bool
+gconfInitPluginForDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ Bool status;
+
+ GCONF_DISPLAY (d);
+
+ UNWRAP (gd, d, initPluginForDisplay);
+ status = (*d->initPluginForDisplay) (p, d);
+ WRAP (gd, d, initPluginForDisplay, gconfInitPluginForDisplay);
+
+ if (status)
+ gconfInitPlugin (d, p);
+
+ if (status && p->vTable->getDisplayOptions)
+ {
+ CompOption *option;
+ int nOption;
+
+ option = (*p->vTable->getDisplayOptions) (d, &nOption);
+ while (nOption--)
+ gconfInitOption (d, option++, "allscreens", p->vTable->name);
+ }
+
+ return status;
+}
+
+static Bool
+gconfInitPluginForScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ Bool status;
+
+ GCONF_SCREEN (s);
+
+ UNWRAP (gs, s, initPluginForScreen);
+ status = (*s->initPluginForScreen) (p, s);
+ WRAP (gs, s, initPluginForScreen, gconfInitPluginForScreen);
+
+ if (status && p->vTable->getScreenOptions)
+ {
+ CompOption *option;
+ int nOption;
+ gchar *screen;
+
+ screen = g_strdup_printf ("screen%d", s->screenNum);
+
+ option = (*p->vTable->getScreenOptions) (s, &nOption);
+ while (nOption--)
+ gconfInitOption (s->display, option++, screen, p->vTable->name);
+
+ g_free (screen);
+ }
+
+ return status;
+}
+
+static void
+gconfKeyChanged (GConfClient *client,
+ guint cnxn_id,
+ GConfEntry *entry,
+ gpointer user_data)
+{
+ CompDisplay *display = (CompDisplay *) user_data;
+
+ gconfGetOptionValue (display, entry);
+}
+
+static Bool
+gconfTimeout (void *closure)
+{
+ while (g_main_pending ()) g_main_iteration (FALSE);
+
+ return TRUE;
+}
+
+static Bool
+gconfInitDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ CompOption *option;
+ int nOption;
+ GConfDisplay *gd;
+
+ gd = malloc (sizeof (GConfDisplay));
+ if (!gd)
+ return FALSE;
+
+ gd->screenPrivateIndex = allocateScreenPrivateIndex (d);
+ if (gd->screenPrivateIndex < 0)
+ {
+ free (gd);
+ return FALSE;
+ }
+
+ g_type_init ();
+
+ gd->client = gconf_client_get_default ();
+
+ gconf_client_add_dir (gd->client, APP_NAME,
+ GCONF_CLIENT_PRELOAD_NONE, NULL);
+
+ WRAP (gd, d, initPluginForDisplay, gconfInitPluginForDisplay);
+ WRAP (gd, d, setDisplayOption, gconfSetDisplayOption);
+ WRAP (gd, d, setDisplayOptionForPlugin, gconfSetDisplayOptionForPlugin);
+
+ d->privates[displayPrivateIndex].ptr = gd;
+
+ option = compGetDisplayOptions (d, &nOption);
+ while (nOption--)
+ gconfInitOption (d, option++, "allscreens", 0);
+
+ gconf_client_notify_add (gd->client, APP_NAME, gconfKeyChanged, d,
+ NULL, NULL);
+
+ gd->timeoutHandle = compAddTimeout (KEY_CHANGE_TIMEOUT, gconfTimeout, 0);
+
+ return TRUE;
+}
+
+static void
+gconfFiniDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ GCONF_DISPLAY (d);
+
+ compRemoveTimeout (gd->timeoutHandle);
+
+ g_object_unref (gd->client);
+
+ freeScreenPrivateIndex (d, gd->screenPrivateIndex);
+
+ free (gd);
+}
+
+static Bool
+gconfInitScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ CompOption *option;
+ int nOption;
+ GConfScreen *gs;
+ gchar *screen;
+
+ GCONF_DISPLAY (s->display);
+
+ gs = malloc (sizeof (GConfScreen));
+ if (!gs)
+ return FALSE;
+
+ WRAP (gs, s, initPluginForScreen, gconfInitPluginForScreen);
+ WRAP (gs, s, setScreenOption, gconfSetScreenOption);
+ WRAP (gs, s, setScreenOptionForPlugin, gconfSetScreenOptionForPlugin);
+
+ s->privates[gd->screenPrivateIndex].ptr = gs;
+
+ screen = g_strdup_printf ("screen%d", s->screenNum);
+
+ option = compGetScreenOptions (s, &nOption);
+ while (nOption--)
+ gconfInitOption (s->display, option++, screen, 0);
+
+ g_free (screen);
+
+ return TRUE;
+}
+
+static void
+gconfFiniScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ GCONF_SCREEN (s);
+
+ UNWRAP (gs, s, initPluginForScreen);
+ UNWRAP (gs, s, setScreenOption);
+ UNWRAP (gs, s, setScreenOptionForPlugin);
+
+ free (gs);
+}
+
+static Bool
+gconfInit (CompPlugin *p)
+{
+ displayPrivateIndex = allocateDisplayPrivateIndex ();
+ if (displayPrivateIndex < 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+gconfFini (CompPlugin *p)
+{
+ if (displayPrivateIndex >= 0)
+ freeDisplayPrivateIndex (displayPrivateIndex);
+}
+
+CompPluginDep gconfDeps[] = {
+ { CompPluginRuleBefore, "decoration" },
+ { CompPluginRuleBefore, "wobbly" },
+ { CompPluginRuleBefore, "fade" },
+ { CompPluginRuleBefore, "cube" },
+ { CompPluginRuleBefore, "expose" }
+};
+
+CompPluginVTable gconfVTable = {
+ "gconf",
+ "GConf",
+ "GConf Control Backend",
+ gconfInit,
+ gconfFini,
+ gconfInitDisplay,
+ gconfFiniDisplay,
+ gconfInitScreen,
+ gconfFiniScreen,
+ 0, /* InitWindow */
+ 0, /* FiniWindow */
+ 0, /* GetDisplayOptions */
+ 0, /* SetDisplayOption */
+ 0, /* GetScreenOptions */
+ 0, /* SetScreenOption */
+ gconfDeps,
+ sizeof (gconfDeps) / sizeof (gconfDeps[0])
+};
+
+CompPluginVTable *
+getCompPluginInfo (void)
+{
+ return &gconfVTable;
+}
diff --git a/plugins/minimize.c b/plugins/minimize.c
new file mode 100644
index 00000000..0fa08973
--- /dev/null
+++ b/plugins/minimize.c
@@ -0,0 +1,820 @@
+/*
+ * 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.h>
+
+#define MIN_SPEED_DEFAULT 1.5f
+#define MIN_SPEED_MIN 0.1f
+#define MIN_SPEED_MAX 50.0f
+#define MIN_SPEED_PRECISION 0.1f
+
+#define MIN_TIMESTEP_DEFAULT 0.5f
+#define MIN_TIMESTEP_MIN 0.1f
+#define MIN_TIMESTEP_MAX 50.0f
+#define MIN_TIMESTEP_PRECISION 0.1f
+
+static char *winType[] = {
+ "Toolbar",
+ "Utility",
+ "Dialog",
+ "Normal"
+};
+#define N_WIN_TYPE (sizeof (winType) / sizeof (winType[0]))
+
+static int displayPrivateIndex;
+
+typedef struct _MinDisplay {
+ int screenPrivateIndex;
+ HandleEventProc handleEvent;
+ Atom winChangeStateAtom;
+ Atom winIconGeometryAtom;
+} MinDisplay;
+
+#define MIN_SCREEN_OPTION_SPEED 0
+#define MIN_SCREEN_OPTION_TIMESTEP 1
+#define MIN_SCREEN_OPTION_WINDOW_TYPE 2
+#define MIN_SCREEN_OPTION_NUM 3
+
+typedef struct _MinScreen {
+ int windowPrivateIndex;
+
+ CompOption opt[MIN_SCREEN_OPTION_NUM];
+
+ PreparePaintScreenProc preparePaintScreen;
+ DonePaintScreenProc donePaintScreen;
+ PaintScreenProc paintScreen;
+ PaintWindowProc paintWindow;
+ DamageWindowRectProc damageWindowRect;
+ FocusWindowProc focusWindow;
+
+ float speed;
+ float timestep;
+
+ unsigned int wMask;
+
+ int moreAdjust;
+} MinScreen;
+
+typedef struct _MinWindow {
+ GLfloat xVelocity, yVelocity, xScaleVelocity, yScaleVelocity;
+ GLfloat xScale, yScale;
+ GLfloat tx, ty;
+
+ Bool adjust;
+
+ XRectangle icon;
+
+ int state, newState;
+
+ int unmapCnt;
+} MinWindow;
+
+#define GET_MIN_DISPLAY(d) \
+ ((MinDisplay *) (d)->privates[displayPrivateIndex].ptr)
+
+#define MIN_DISPLAY(d) \
+ MinDisplay *md = GET_MIN_DISPLAY (d)
+
+#define GET_MIN_SCREEN(s, md) \
+ ((MinScreen *) (s)->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)->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 (CompScreen *screen,
+ int *count)
+{
+ MIN_SCREEN (screen);
+
+ *count = NUM_OPTIONS (ms);
+ return ms->opt;
+}
+
+static Bool
+minSetScreenOption (CompScreen *screen,
+ 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_SPEED:
+ if (compSetFloatOption (o, value))
+ {
+ ms->speed = o->value.f;
+ return TRUE;
+ }
+ break;
+ case MIN_SCREEN_OPTION_TIMESTEP:
+ if (compSetFloatOption (o, value))
+ {
+ ms->timestep = o->value.f;
+ return TRUE;
+ }
+ break;
+ case MIN_SCREEN_OPTION_WINDOW_TYPE:
+ if (compSetOptionList (o, value))
+ {
+ ms->wMask = compWindowTypeMaskFromStringList (&o->value);
+ return TRUE;
+ }
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+static void
+minScreenInitOptions (MinScreen *ms)
+{
+ CompOption *o;
+ int i;
+
+ o = &ms->opt[MIN_SCREEN_OPTION_SPEED];
+ o->name = "speed";
+ o->shortDesc = "Speed";
+ o->longDesc = "Minimize speed";
+ o->type = CompOptionTypeFloat;
+ o->value.f = MIN_SPEED_DEFAULT;
+ o->rest.f.min = MIN_SPEED_MIN;
+ o->rest.f.max = MIN_SPEED_MAX;
+ o->rest.f.precision = MIN_SPEED_PRECISION;
+
+ o = &ms->opt[MIN_SCREEN_OPTION_TIMESTEP];
+ o->name = "timestep";
+ o->shortDesc = "Timestep";
+ o->longDesc = "Minimize timestep";
+ o->type = CompOptionTypeFloat;
+ o->value.f = MIN_TIMESTEP_DEFAULT;
+ o->rest.f.min = MIN_TIMESTEP_MIN;
+ o->rest.f.max = MIN_TIMESTEP_MAX;
+ o->rest.f.precision = MIN_TIMESTEP_PRECISION;
+
+ o = &ms->opt[MIN_SCREEN_OPTION_WINDOW_TYPE];
+ o->name = "window_types";
+ o->shortDesc = "Window Types";
+ o->longDesc = "Window types that should be transformed when "
+ "minimized";
+ o->type = CompOptionTypeList;
+ o->value.list.type = CompOptionTypeString;
+ o->value.list.nValue = N_WIN_TYPE;
+ o->value.list.value = malloc (sizeof (CompOptionValue) * N_WIN_TYPE);
+ for (i = 0; i < N_WIN_TYPE; i++)
+ o->value.list.value[i].s = strdup (winType[i]);
+ o->rest.s.string = windowTypeString;
+ o->rest.s.nString = nWindowTypeString;
+
+ ms->wMask = compWindowTypeMaskFromStringList (&o->value);
+}
+
+static Bool
+minGetWindowIconGeometry (CompWindow *w,
+ XRectangle *rect)
+{
+ Atom actual;
+ int result, format;
+ unsigned long n, left;
+ unsigned char *data;
+
+ MIN_DISPLAY (w->screen->display);
+
+ result = XGetWindowProperty (w->screen->display->display, w->id,
+ md->winIconGeometryAtom,
+ 0L, 4L, FALSE, XA_CARDINAL, &actual, &format,
+ &n, &left, &data);
+
+ if (result == Success && n && data)
+ {
+ if (n == 4)
+ {
+ unsigned long *geometry = (unsigned long *) data;
+
+ rect->x = geometry[0];
+ rect->y = geometry[1];
+ rect->width = geometry[2];
+ rect->height = geometry[3];
+
+ XFree (data);
+
+ return TRUE;
+ }
+
+ XFree (data);
+ }
+
+ return FALSE;
+}
+
+static int
+minGetWindowState (CompWindow *w)
+{
+ Atom actual;
+ int result, format;
+ unsigned long n, left;
+ unsigned char *data;
+
+ 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 && n && data)
+ {
+ int state;
+
+ memcpy (&state, data, sizeof (int));
+ XFree ((void *) data);
+
+ return state;
+ }
+
+ return WithdrawnState;
+}
+
+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 = mw->icon.x;
+ y1 = mw->icon.y;
+ xScale = (float) mw->icon.width / w->width;
+ yScale = (float) mw->icon.height / w->height;
+ }
+ else
+ {
+ x1 = w->serverX;
+ y1 = w->serverY;
+ xScale = yScale = 1.0f;
+ }
+
+ dx = x1 - w->attrib.x;
+
+ 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;
+
+ 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, dx, dy;
+ float amount, chunk;
+
+ amount = msSinceLastPaint * 0.05f * ms->speed;
+ steps = amount / (0.5f * ms->timestep);
+ 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;
+
+ dx = (w->serverX + mw->tx) - w->attrib.x;
+ dy = (w->serverY + mw->ty) - w->attrib.y;
+
+ moveWindow (w, dx, dy, FALSE);
+
+ (*s->setWindowScale) (w, mw->xScale, mw->yScale);
+
+ if (!mw->adjust)
+ {
+ mw->state = mw->newState;
+
+ while (mw->unmapCnt)
+ {
+ unmapWindow (w);
+ mw->unmapCnt--;
+ }
+ }
+ }
+ }
+
+ if (!ms->moreAdjust)
+ break;
+ }
+
+ if (ms->moreAdjust)
+ {
+ for (w = s->windows; w; w = w->next)
+ {
+ MIN_WINDOW (w);
+
+ if (mw->adjust)
+ 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;
+
+ for (w = s->windows; w; w = w->next)
+ {
+ MIN_WINDOW (w);
+
+ if (mw->adjust)
+ addWindowDamage (w);
+ }
+ }
+
+ UNWRAP (ms, s, donePaintScreen);
+ (*s->donePaintScreen) (s);
+ WRAP (ms, s, donePaintScreen, minDonePaintScreen);
+}
+
+static Bool
+minPaintScreen (CompScreen *s,
+ const ScreenPaintAttrib *sAttrib,
+ Region region,
+ unsigned int mask)
+{
+ Bool status;
+
+ MIN_SCREEN (s);
+
+ if (ms->moreAdjust)
+ mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS_MASK;
+
+ UNWRAP (ms, s, paintScreen);
+ status = (*s->paintScreen) (s, sAttrib, region, mask);
+ WRAP (ms, s, paintScreen, minPaintScreen);
+
+ return status;
+}
+
+static Bool
+minPaintWindow (CompWindow *w,
+ const WindowPaintAttrib *attrib,
+ Region region,
+ unsigned int mask)
+{
+ CompScreen *s = w->screen;
+ Bool status;
+
+ MIN_SCREEN (s);
+ MIN_WINDOW (w);
+
+ if (mw->adjust)
+ mask |= PAINT_WINDOW_TRANSFORMED_MASK;
+
+ UNWRAP (ms, s, paintWindow);
+ status = (*s->paintWindow) (w, attrib, 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;
+
+ while (mw->unmapCnt)
+ {
+ unmapWindow (w);
+ mw->unmapCnt--;
+ }
+ }
+ break;
+ case UnmapNotify:
+ w = findWindowAtDisplay (d, event->xunmap.window);
+ if (w && (w->state & CompWindowStateHiddenMask))
+ {
+ MIN_SCREEN (w->screen);
+
+ if (!w->invisible && (ms->wMask & w->type))
+ {
+ MIN_WINDOW (w);
+
+ mw->newState = IconicState;
+
+ if (minGetWindowIconGeometry (w, &mw->icon))
+ {
+ mw->adjust = TRUE;
+ ms->moreAdjust = TRUE;
+
+ mw->unmapCnt++;
+ w->unmapRefCnt++;
+
+ addWindowDamage (w);
+ }
+ else
+ {
+ mw->state = mw->newState;
+ }
+ }
+ }
+ 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;
+
+ MIN_SCREEN (w->screen);
+
+ if (initial && !w->invisible && (ms->wMask & w->type))
+ {
+ MIN_WINDOW (w);
+
+ if (mw->state == IconicState)
+ {
+ if (minGetWindowIconGeometry (w, &mw->icon))
+ {
+ if (!mw->adjust)
+ {
+ mw->adjust = TRUE;
+ ms->moreAdjust = TRUE;
+
+ mw->tx = mw->icon.x - w->serverX;
+ mw->ty = mw->icon.y - w->serverY;
+ mw->xScale = (float) mw->icon.width / w->width;
+ mw->yScale = (float) mw->icon.height / w->height;
+
+ moveWindow (w,
+ mw->icon.x - w->attrib.x,
+ mw->icon.y - w->attrib.y,
+ FALSE);
+
+ (*w->screen->setWindowScale) (w, mw->xScale, mw->yScale);
+
+ addWindowDamage (w);
+ }
+ }
+ }
+
+ mw->newState = NormalState;
+ }
+
+ 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;
+
+ 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);
+ md->winIconGeometryAtom =
+ XInternAtom (d->display, "_NET_WM_ICON_GEOMETRY", 0);
+
+ WRAP (md, d, handleEvent, minHandleEvent);
+
+ d->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 Bool
+minInitScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ MinScreen *ms;
+
+ MIN_DISPLAY (s->display);
+
+ ms = malloc (sizeof (MinScreen));
+ if (!ms)
+ return FALSE;
+
+ ms->windowPrivateIndex = allocateWindowPrivateIndex (s);
+ if (ms->windowPrivateIndex < 0)
+ {
+ free (ms);
+ return FALSE;
+ }
+
+ ms->moreAdjust = FALSE;
+
+ ms->speed = MIN_SPEED_DEFAULT;
+ ms->timestep = MIN_TIMESTEP_DEFAULT;
+
+ minScreenInitOptions (ms);
+
+ WRAP (ms, s, preparePaintScreen, minPreparePaintScreen);
+ WRAP (ms, s, donePaintScreen, minDonePaintScreen);
+ WRAP (ms, s, paintScreen, minPaintScreen);
+ WRAP (ms, s, paintWindow, minPaintWindow);
+ WRAP (ms, s, damageWindowRect, minDamageWindowRect);
+ WRAP (ms, s, focusWindow, minFocusWindow);
+
+ s->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, paintScreen);
+ UNWRAP (ms, s, paintWindow);
+ UNWRAP (ms, s, damageWindowRect);
+ UNWRAP (ms, s, focusWindow);
+
+ 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->state = mw->newState = minGetWindowState (w);
+
+ w->privates[ms->windowPrivateIndex].ptr = mw;
+
+ return TRUE;
+}
+
+static void
+minFiniWindow (CompPlugin *p,
+ CompWindow *w)
+{
+ MIN_WINDOW (w);
+
+ while (mw->unmapCnt--)
+ unmapWindow (w);
+
+ free (mw);
+}
+
+static Bool
+minInit (CompPlugin *p)
+{
+ displayPrivateIndex = allocateDisplayPrivateIndex ();
+ if (displayPrivateIndex < 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+minFini (CompPlugin *p)
+{
+ if (displayPrivateIndex >= 0)
+ freeDisplayPrivateIndex (displayPrivateIndex);
+}
+
+CompPluginDep minDeps[] = {
+ { CompPluginRuleBefore, "cube" },
+ { CompPluginRuleBefore, "expose" }
+};
+
+static CompPluginVTable minVTable = {
+ "minimize",
+ "Minimize Effect",
+ "Transform windows when they are minimized and unminimized",
+ minInit,
+ minFini,
+ minInitDisplay,
+ minFiniDisplay,
+ minInitScreen,
+ minFiniScreen,
+ minInitWindow,
+ minFiniWindow,
+ 0, /* GetDisplayOptions */
+ 0, /* SetDisplayOption */
+ minGetScreenOptions,
+ minSetScreenOption,
+ minDeps,
+ sizeof (minDeps) / sizeof (minDeps[0])
+};
+
+CompPluginVTable *
+getCompPluginInfo (void)
+{
+ return &minVTable;
+}
diff --git a/plugins/move.c b/plugins/move.c
new file mode 100644
index 00000000..7b1e255a
--- /dev/null
+++ b/plugins/move.c
@@ -0,0 +1,549 @@
+/*
+ * 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.h>
+
+#define MOVE_INITIATE_BUTTON_DEFAULT Button1
+#define MOVE_INITIATE_MODIFIERS_DEFAULT (CompPressMask | CompAltMask)
+
+#define MOVE_TERMINATE_BUTTON_DEFAULT Button1
+#define MOVE_TERMINATE_MODIFIERS_DEFAULT CompReleaseMask
+
+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
+
+static int displayPrivateIndex;
+
+typedef struct _MoveDisplay {
+ int screenPrivateIndex;
+ HandleEventProc handleEvent;
+
+ CompWindow *w;
+ KeyCode key[NUM_KEYS];
+} MoveDisplay;
+
+#define MOVE_SCREEN_OPTION_INITIATE 0
+#define MOVE_SCREEN_OPTION_TERMINATE 1
+#define MOVE_SCREEN_OPTION_NUM 2
+
+typedef struct _MoveScreen {
+ CompOption opt[MOVE_SCREEN_OPTION_NUM];
+
+ int grabIndex;
+
+ Cursor moveCursor;
+
+ int prevPointerX;
+ int prevPointerY;
+} MoveScreen;
+
+#define GET_MOVE_DISPLAY(d) \
+ ((MoveDisplay *) (d)->privates[displayPrivateIndex].ptr)
+
+#define MOVE_DISPLAY(d) \
+ MoveDisplay *md = GET_MOVE_DISPLAY (d)
+
+#define GET_MOVE_SCREEN(s, md) \
+ ((MoveScreen *) (s)->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 CompOption *
+moveGetScreenOptions (CompScreen *screen,
+ int *count)
+{
+ MOVE_SCREEN (screen);
+
+ *count = NUM_OPTIONS (ms);
+ return ms->opt;
+}
+
+static Bool
+moveSetScreenOption (CompScreen *screen,
+ char *name,
+ CompOptionValue *value)
+{
+ CompOption *o;
+ int index;
+
+ MOVE_SCREEN (screen);
+
+ o = compFindOption (ms->opt, NUM_OPTIONS (ms), name, &index);
+ if (!o)
+ return FALSE;
+
+ switch (index) {
+ case MOVE_SCREEN_OPTION_INITIATE:
+ if (addScreenBinding (screen, &value->bind))
+ {
+ removeScreenBinding (screen, &o->value.bind);
+
+ if (compSetBindingOption (o, value))
+ return TRUE;
+ }
+ break;
+ case MOVE_SCREEN_OPTION_TERMINATE:
+ if (compSetBindingOption (o, value))
+ return TRUE;
+ break;
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+static void
+moveScreenInitOptions (MoveScreen *ms,
+ Display *display)
+{
+ CompOption *o;
+
+ o = &ms->opt[MOVE_SCREEN_OPTION_INITIATE];
+ o->name = "initiate";
+ o->shortDesc = "Initiate Window Move";
+ o->longDesc = "Start moving window";
+ o->type = CompOptionTypeBinding;
+ o->value.bind.type = CompBindingTypeButton;
+ o->value.bind.u.button.modifiers = MOVE_INITIATE_MODIFIERS_DEFAULT;
+ o->value.bind.u.button.button = MOVE_INITIATE_BUTTON_DEFAULT;
+
+ o = &ms->opt[MOVE_SCREEN_OPTION_TERMINATE];
+ o->name = "terminate";
+ o->shortDesc = "Terminate Window Move";
+ o->longDesc = "Stop moving window";
+ o->type = CompOptionTypeBinding;
+ o->value.bind.type = CompBindingTypeButton;
+ o->value.bind.u.button.modifiers = MOVE_TERMINATE_MODIFIERS_DEFAULT;
+ o->value.bind.u.button.button = MOVE_TERMINATE_BUTTON_DEFAULT;
+}
+
+static void
+moveInitiate (CompWindow *w,
+ int x,
+ int y,
+ unsigned int state)
+{
+ MOVE_DISPLAY (w->screen->display);
+ MOVE_SCREEN (w->screen);
+
+ /* some plugin has already grabbed the screen */
+ if (w->screen->maxGrab)
+ return;
+
+ if (md->w)
+ return;
+
+ if (w->type & (CompWindowTypeDesktopMask |
+ CompWindowTypeDockMask |
+ CompWindowTypeFullscreenMask))
+ return;
+
+ if (w->attrib.override_redirect)
+ return;
+
+ md->w = w;
+
+ ms->prevPointerX = x;
+ ms->prevPointerY = y;
+
+ if (!ms->grabIndex)
+ ms->grabIndex = pushScreenGrab (w->screen, ms->moveCursor);
+
+ if (ms->grabIndex)
+ (w->screen->windowGrabNotify) (w, x, y, state,
+ CompWindowGrabMoveMask |
+ CompWindowGrabButtonMask);
+}
+
+static void
+moveTerminate (CompDisplay *d)
+{
+ MOVE_DISPLAY (d);
+
+ if (md->w)
+ {
+ MOVE_SCREEN (md->w->screen);
+
+ if (ms->grabIndex)
+ {
+ removeScreenGrab (md->w->screen, ms->grabIndex, NULL);
+ ms->grabIndex = 0;
+ }
+
+ (md->w->screen->windowUngrabNotify) (md->w);
+
+ syncWindowPosition (md->w);
+ md->w = 0;
+ }
+}
+
+static void
+moveHandleMotionEvent (CompScreen *s,
+ int xRoot,
+ int yRoot)
+{
+ MOVE_SCREEN (s);
+
+ if (ms->grabIndex)
+ {
+ CompWindow *w;
+ int pointerDx, pointerDy;
+
+ MOVE_DISPLAY (s->display);
+
+ w = md->w;
+
+ pointerDx = xRoot - ms->prevPointerX;
+ pointerDy = yRoot - ms->prevPointerY;
+ ms->prevPointerX = xRoot;
+ ms->prevPointerY = yRoot;
+
+ if (w->type & CompWindowTypeFullscreenMask)
+ {
+ pointerDx = pointerDy = 0;
+ }
+ else
+ {
+ int min, max;
+
+ if (w->state & CompWindowStateMaximizedVertMask)
+ {
+ min = s->workArea.y + w->input.top;
+ max = s->workArea.y + s->workArea.height -
+ w->input.bottom - w->height;
+
+ if (w->attrib.y + pointerDy < min)
+ pointerDy = min - w->attrib.y;
+ else if (w->attrib.y + pointerDy > max)
+ pointerDy = max - w->attrib.y;
+ }
+
+ if (w->state & CompWindowStateMaximizedHorzMask)
+ {
+ min = s->workArea.x + w->input.left;
+ max = s->workArea.x + s->workArea.width -
+ w->input.right - w->width;
+
+ if (w->attrib.x + pointerDx < min)
+ pointerDx = min - w->attrib.x;
+ else if (w->attrib.x + pointerDx > max)
+ pointerDx = max - w->attrib.x;
+ }
+ }
+
+ if (pointerDx || pointerDy)
+ moveWindow (md->w, pointerDx, pointerDy, TRUE);
+ }
+}
+
+static void
+moveHandleEvent (CompDisplay *d,
+ XEvent *event)
+{
+ CompScreen *s;
+
+ MOVE_DISPLAY (d);
+
+ switch (event->type) {
+ case KeyPress:
+ case KeyRelease:
+ s = findScreenAtDisplay (d, event->xkey.root);
+ if (s)
+ {
+ MOVE_SCREEN (s);
+
+ if (EV_KEY (&ms->opt[MOVE_SCREEN_OPTION_INITIATE], event))
+ {
+ CompWindow *w;
+
+ w = findWindowAtScreen (s, event->xkey.window);
+ if (w)
+ moveInitiate (w,
+ event->xkey.x_root,
+ event->xkey.y_root,
+ event->xkey.state);
+ }
+
+ if (EV_KEY (&ms->opt[MOVE_SCREEN_OPTION_TERMINATE], event) ||
+ (event->type == KeyPress &&
+ event->xkey.keycode == s->escapeKeyCode))
+ moveTerminate (d);
+
+ if (ms->grabIndex && event->type == KeyPress)
+ {
+ 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 ButtonPress:
+ case ButtonRelease:
+ s = findScreenAtDisplay (d, event->xbutton.root);
+ if (s)
+ {
+ CompWindow *w;
+
+ MOVE_SCREEN (s);
+
+ if (EV_BUTTON (&ms->opt[MOVE_SCREEN_OPTION_INITIATE], event))
+ {
+ w = findTopLevelWindowAtScreen (s, event->xbutton.window);
+ if (w)
+ moveInitiate (w,
+ event->xbutton.x_root,
+ event->xbutton.y_root,
+ event->xbutton.state);
+ }
+
+ if (EV_BUTTON (&ms->opt[MOVE_SCREEN_OPTION_TERMINATE], event))
+ moveTerminate (d);
+ }
+ break;
+ case MotionNotify:
+ s = findScreenAtDisplay (d, event->xmotion.root);
+ if (s)
+ moveHandleMotionEvent (s,
+ event->xmotion.x_root,
+ event->xmotion.y_root);
+ 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)
+ {
+ int xRoot, yRoot;
+
+ if (event->xclient.data.l[2] == WmMoveResizeMoveKeyboard)
+ {
+ xRoot = w->attrib.x + w->width / 2;
+ yRoot = w->attrib.y + w->height / 2;
+
+ XWarpPointer (d->display, None, w->screen->root,
+ 0, 0, 0, 0, xRoot, yRoot);
+
+ moveInitiate (w, xRoot, yRoot, 0);
+ }
+ else
+ {
+ unsigned int state;
+ Window root, child;
+ int i;
+
+ XQueryPointer (d->display, w->screen->root,
+ &root, &child, &xRoot, &yRoot,
+ &i, &i, &state);
+
+ /* TODO: not only button 1*/
+ if (state & Button1Mask)
+ {
+ moveInitiate (w,
+ event->xclient.data.l[0],
+ event->xclient.data.l[1],
+ state | CompPressMask);
+
+ moveHandleMotionEvent (w->screen, xRoot, yRoot);
+ }
+ }
+ }
+ }
+ }
+ break;
+ case DestroyNotify:
+ if (md->w && md->w->id == event->xdestroywindow.window)
+ moveTerminate (d);
+ break;
+ case UnmapNotify:
+ if (md->w && md->w->id == event->xunmap.window)
+ moveTerminate (d);
+ default:
+ break;
+ }
+
+ UNWRAP (md, d, handleEvent);
+ (*d->handleEvent) (d, event);
+ WRAP (md, d, handleEvent, moveHandleEvent);
+}
+
+static Bool
+moveInitDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ MoveDisplay *md;
+ int i;
+
+ md = malloc (sizeof (MoveDisplay));
+ if (!md)
+ return FALSE;
+
+ md->screenPrivateIndex = allocateScreenPrivateIndex (d);
+ if (md->screenPrivateIndex < 0)
+ {
+ free (md);
+ return FALSE;
+ }
+
+ md->w = 0;
+
+ for (i = 0; i < NUM_KEYS; i++)
+ md->key[i] = XKeysymToKeycode (d->display,
+ XStringToKeysym (mKeys[i].name));
+
+ WRAP (md, d, handleEvent, moveHandleEvent);
+
+ d->privates[displayPrivateIndex].ptr = md;
+
+ return TRUE;
+}
+
+static void
+moveFiniDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ MOVE_DISPLAY (d);
+
+ freeScreenPrivateIndex (d, md->screenPrivateIndex);
+
+ UNWRAP (md, d, handleEvent);
+
+ 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->prevPointerX = 0;
+ ms->prevPointerY = 0;
+
+ moveScreenInitOptions (ms, s->display->display);
+
+ ms->moveCursor = XCreateFontCursor (s->display->display, XC_plus);
+
+ addScreenBinding (s, &ms->opt[MOVE_SCREEN_OPTION_INITIATE].value.bind);
+
+ s->privates[md->screenPrivateIndex].ptr = ms;
+
+ return TRUE;
+}
+
+static void
+moveFiniScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ MOVE_SCREEN (s);
+
+ free (ms);
+}
+
+static Bool
+moveInit (CompPlugin *p)
+{
+ displayPrivateIndex = allocateDisplayPrivateIndex ();
+ if (displayPrivateIndex < 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+moveFini (CompPlugin *p)
+{
+ if (displayPrivateIndex >= 0)
+ freeDisplayPrivateIndex (displayPrivateIndex);
+}
+
+CompPluginVTable moveVTable = {
+ "move",
+ "Move Window",
+ "Move window",
+ moveInit,
+ moveFini,
+ moveInitDisplay,
+ moveFiniDisplay,
+ moveInitScreen,
+ moveFiniScreen,
+ 0, /* InitWindow */
+ 0, /* FiniWindow */
+ 0, /* GetDisplayOptions */
+ 0, /* SetDisplayOption */
+ moveGetScreenOptions,
+ moveSetScreenOption,
+ NULL,
+ 0
+};
+
+CompPluginVTable *
+getCompPluginInfo (void)
+{
+ return &moveVTable;
+}
diff --git a/plugins/place.c b/plugins/place.c
new file mode 100644
index 00000000..aef3480a
--- /dev/null
+++ b/plugins/place.c
@@ -0,0 +1,1081 @@
+/*
+ * 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 <glib.h>
+
+#include <compiz.h>
+
+#define PLACE_WORKAROUND_DEFAULT TRUE
+
+static int displayPrivateIndex;
+
+typedef struct _PlaceDisplay {
+ int screenPrivateIndex;
+ HandleEventProc handleEvent;
+} PlaceDisplay;
+
+#define PLACE_SCREEN_OPTION_WORKAROUND 0
+#define PLACE_SCREEN_OPTION_NUM 1
+
+typedef struct _PlaceScreen {
+ CompOption opt[PLACE_SCREEN_OPTION_NUM];
+
+ DamageWindowRectProc damageWindowRect;
+} PlaceScreen;
+
+#define GET_PLACE_DISPLAY(d) \
+ ((PlaceDisplay *) (d)->privates[displayPrivateIndex].ptr)
+
+#define PLACE_DISPLAY(d) \
+ PlaceDisplay *pd = GET_PLACE_DISPLAY (d)
+
+#define GET_PLACE_SCREEN(s, pd) \
+ ((PlaceScreen *) (s)->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))
+
+static CompOption *
+placeGetScreenOptions (CompScreen *screen,
+ int *count)
+{
+ PLACE_SCREEN (screen);
+
+ *count = NUM_OPTIONS (ps);
+ return ps->opt;
+}
+
+static Bool
+placeSetScreenOption (CompScreen *screen,
+ 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_WORKAROUND:
+ if (compSetBoolOption (o, value))
+ return TRUE;
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+static void
+placeScreenInitOptions (PlaceScreen *ps)
+{
+ CompOption *o;
+
+ o = &ps->opt[PLACE_SCREEN_OPTION_WORKAROUND];
+ o->name = "workarounds";
+ o->shortDesc = "Workarounds";
+ o->longDesc = "Window placement workarounds";
+ o->type = CompOptionTypeBool;
+ o->value.b = PLACE_WORKAROUND_DEFAULT;
+}
+
+typedef enum {
+ PlaceLeft,
+ PlaceRight,
+ PlaceTop,
+ PlaceBottom
+} PlaceWindowDirection;
+
+static Bool
+rectangleIntersect (XRectangle *src1,
+ XRectangle *src2,
+ XRectangle *dest)
+{
+ int dest_x, dest_y;
+ int dest_w, dest_h;
+ int return_val;
+
+ g_return_val_if_fail (src1 != NULL, FALSE);
+ g_return_val_if_fail (src2 != NULL, FALSE);
+ g_return_val_if_fail (dest != NULL, FALSE);
+
+ return_val = FALSE;
+
+ dest_x = MAX (src1->x, src2->x);
+ dest_y = MAX (src1->y, src2->y);
+ dest_w = MIN (src1->x + src1->width, src2->x + src2->width) - dest_x;
+ dest_h = MIN (src1->y + src1->height, src2->y + src2->height) - dest_y;
+
+ if (dest_w > 0 && dest_h > 0)
+ {
+ dest->x = dest_x;
+ dest->y = dest_y;
+ dest->width = dest_w;
+ dest->height = dest_h;
+ return_val = TRUE;
+ }
+ else
+ {
+ dest->width = 0;
+ dest->height = 0;
+ }
+
+ return return_val;
+}
+
+static gint
+northwestcmp (gconstpointer a,
+ gconstpointer b)
+{
+ CompWindow *aw = (gpointer) a;
+ CompWindow *bw = (gpointer) b;
+ int from_origin_a;
+ int from_origin_b;
+ int ax, ay, bx, by;
+
+ ax = aw->attrib.x - aw->input.left;
+ ay = aw->attrib.y - aw->input.top;
+
+ bx = bw->attrib.x - bw->input.left;
+ by = bw->attrib.y - bw->input.top;
+
+ /* probably there's a fast good-enough-guess we could use here. */
+ from_origin_a = sqrt (ax * ax + ay * ay);
+ from_origin_b = sqrt (bx * bx + by * by);
+
+ if (from_origin_a < from_origin_b)
+ return -1;
+ else if (from_origin_a > from_origin_b)
+ return 1;
+ else
+ return 0;
+}
+
+static void
+find_next_cascade (CompWindow *window,
+ GList *windows,
+ int x,
+ int y,
+ int *new_x,
+ int *new_y)
+{
+ GList *tmp;
+ GList *sorted;
+ int cascade_x, cascade_y;
+ int x_threshold, y_threshold;
+ int window_width, window_height;
+ int cascade_stage;
+ XRectangle work_area;
+
+ sorted = g_list_copy (windows);
+ sorted = g_list_sort (sorted, northwestcmp);
+
+ /* 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
+
+ x_threshold = MAX (window->input.left, CASCADE_FUZZ);
+ y_threshold = MAX (window->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.
+ */
+
+ work_area = window->screen->workArea;
+
+ cascade_x = MAX (0, work_area.x);
+ cascade_y = MAX (0, work_area.y);
+
+ /* Find first cascade position that's not used. */
+
+ window_width = window->attrib.width + window->input.left +
+ window->input.right;
+ window_height = window->attrib.height + window->input.top +
+ window->input.bottom;
+
+ cascade_stage = 0;
+ tmp = sorted;
+ while (tmp != NULL)
+ {
+ CompWindow *w;
+ int wx, wy;
+
+ w = tmp->data;
+
+ /* we want frame position, not window position */
+ wx = w->attrib.x - w->input.left;
+ wy = w->attrib.y - w->input.top;
+
+ if (ABS (wx - cascade_x) < x_threshold &&
+ ABS (wy - cascade_y) < y_threshold)
+ {
+ /* 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 = w->attrib.x;
+ wy = w->attrib.y;
+
+ cascade_x = wx;
+ cascade_y = wy;
+
+ /* If we go off the screen, start over with a new cascade */
+ if (((cascade_x + window_width) >
+ (work_area.x + work_area.width)) ||
+ ((cascade_y + window_height) >
+ (work_area.y + work_area.height)))
+ {
+ cascade_x = MAX (0, work_area.x);
+ cascade_y = MAX (0, work_area.y);
+
+#define CASCADE_INTERVAL 50 /* space between top-left corners of cascades */
+
+ cascade_stage += 1;
+ cascade_x += CASCADE_INTERVAL * cascade_stage;
+
+ /* start over with a new cascade translated to the right,
+ * unless we are out of space
+ */
+ if ((cascade_x + window_width) <
+ (work_area.x + work_area.width))
+ {
+ tmp = sorted;
+ continue;
+ }
+ else
+ {
+ /* All out of space, this cascade_x won't work */
+ cascade_x = MAX (0, work_area.x);
+ break;
+ }
+ }
+ }
+ else
+ {
+ /* Keep searching for a further-down-the-diagonal window. */
+ }
+
+ tmp = tmp->next;
+ }
+
+ /* cascade_x and cascade_y will match the last window in the list
+ * that was "in the way" (in the approximate cascade diagonal)
+ */
+
+ g_list_free (sorted);
+
+ /* Convert coords to position of window, not position of frame. */
+ *new_x = cascade_x + window->input.left;
+ *new_y = cascade_y + window->input.top;
+}
+
+static void
+find_most_freespace (CompWindow *window,
+ CompWindow *focus_window,
+ int x,
+ int y,
+ int *new_x,
+ int *new_y)
+{
+ PlaceWindowDirection side;
+ int max_area;
+ int max_width, max_height, left, right, top, bottom;
+ int left_space, right_space, top_space, bottom_space;
+ int frame_size_left, frame_size_top;
+ XRectangle work_area;
+ XRectangle avoid;
+ XRectangle outer;
+
+ frame_size_left = window->input.left;
+ frame_size_top = window->input.top;
+
+ work_area = window->screen->workArea;
+
+ getOuterRectOfWindow (focus_window, &avoid);
+ getOuterRectOfWindow (window, &outer);
+
+ /* Find the areas of choosing the various sides of the focus window */
+ max_width = MIN (avoid.width, outer.width);
+ max_height = MIN (avoid.height, outer.height);
+ left_space = avoid.x - work_area.x;
+ right_space = work_area.width - (avoid.x + avoid.width - work_area.x);
+ top_space = avoid.y - work_area.y;
+ bottom_space = work_area.height - (avoid.y + avoid.height - work_area.y);
+ left = MIN (left_space, outer.width);
+ right = MIN (right_space, outer.width);
+ top = MIN (top_space, outer.height);
+ bottom = MIN (bottom_space, outer.height);
+
+ /* Find out which side of the focus_window can show the most of the
+ * window
+ */
+ side = PlaceLeft;
+ max_area = left * max_height;
+ if (right * max_height > max_area)
+ {
+ side = PlaceRight;
+ max_area = right * max_height;
+ }
+ if (top * max_width > max_area)
+ {
+ side = PlaceTop;
+ max_area = top * max_width;
+ }
+ if (bottom * max_width > max_area)
+ {
+ side = PlaceBottom;
+ max_area = bottom * max_width;
+ }
+
+ /* Give up if there's no where to put it
+ * (i.e. focus window is maximized)
+ */
+ if (max_area == 0)
+ return;
+
+ /* Place the window on the relevant side; if the whole window fits,
+ * make it adjacent to the focus window; if not, make sure the
+ * window doesn't go off the edge of the screen.
+ */
+ switch (side) {
+ case PlaceLeft:
+ *new_y = avoid.y + frame_size_top;
+ if (left_space > outer.width)
+ *new_x = avoid.x - outer.width + frame_size_left;
+ else
+ *new_x = work_area.x + frame_size_left;
+ break;
+ case PlaceRight:
+ *new_y = avoid.y + frame_size_top;
+ if (right_space > outer.width)
+ *new_x = avoid.x + avoid.width + frame_size_left;
+ else
+ *new_x = work_area.x + work_area.width - outer.width +
+ frame_size_left;
+ break;
+ case PlaceTop:
+ *new_x = avoid.x + frame_size_left;
+ if (top_space > outer.height)
+ *new_y = avoid.y - outer.height + frame_size_top;
+ else
+ *new_y = work_area.y + frame_size_top;
+ break;
+ case PlaceBottom:
+ *new_x = avoid.x + frame_size_left;
+ if (bottom_space > outer.height)
+ *new_y = avoid.y + avoid.height + frame_size_top;
+ else
+ *new_y = work_area.y + work_area.height - outer.height +
+ frame_size_top;
+ break;
+ }
+}
+
+static void
+avoid_being_obscured_as_second_modal_dialog (CompWindow *window,
+ int *x,
+ int *y)
+{
+ /* We can't center this dialog if it was denied focus and it
+ * overlaps with the focus window and this dialog is modal and this
+ * dialog is in the same app as the focus window (*phew*...please
+ * don't make me say that ten times fast). See bug 307875 comment 11
+ * and 12 for details, but basically it means this is probably a
+ * second modal dialog for some app while the focus window is the
+ * first modal dialog. We should probably make them simultaneously
+ * visible in general, but it becomes mandatory to do so due to
+ * buggy apps (e.g. those using gtk+ *sigh*) because in those cases
+ * this second modal dialog also happens to be modal to the first
+ * dialog in addition to the main window, while it has only let us
+ * know about the modal-to-the-main-window part.
+ */
+
+ CompWindow *focus_window;
+
+ focus_window =
+ findWindowAtDisplay (window->screen->display,
+ window->screen->display->activeWindow);
+
+ if (focus_window &&
+ (window->state & CompWindowStateModalMask) &&
+ 0 /* window->denied_focus_and_not_transient &&
+ window_same_application (window, focus_window) &&
+ window_intersect (window, focus_window) */
+ )
+ {
+ find_most_freespace (window, focus_window, *x, *y, x, y);
+ }
+}
+
+static gboolean
+rectangle_overlaps_some_window (XRectangle *rect,
+ GList *windows)
+{
+ GList *tmp;
+ XRectangle dest;
+
+ tmp = windows;
+ while (tmp != NULL)
+ {
+ CompWindow *other = tmp->data;
+ XRectangle other_rect;
+
+ 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:
+ getOuterRectOfWindow (other, &other_rect);
+
+ if (rectangleIntersect (rect, &other_rect, &dest))
+ return TRUE;
+ break;
+ }
+
+ tmp = tmp->next;
+ }
+
+ return FALSE;
+}
+
+static gint
+leftmost_cmp (gconstpointer a,
+ gconstpointer b)
+{
+ CompWindow *aw = (gpointer) a;
+ CompWindow *bw = (gpointer) b;
+ int ax, bx;
+
+ ax = aw->attrib.x - aw->input.left;
+ bx = bw->attrib.x - bw->input.left;
+
+ if (ax < bx)
+ return -1;
+ else if (ax > bx)
+ return 1;
+ else
+ return 0;
+}
+
+static gint
+topmost_cmp (gconstpointer a,
+ gconstpointer b)
+{
+ CompWindow *aw = (gpointer) a;
+ CompWindow *bw = (gpointer) b;
+ int ay, by;
+
+ ay = aw->attrib.y - aw->input.top;
+ by = bw->attrib.y - bw->input.top;
+
+ if (ay < by)
+ return -1;
+ else if (ay > by)
+ return 1;
+ else
+ return 0;
+}
+
+static void
+center_tile_rect_in_area (XRectangle *rect,
+ XRectangle *work_area)
+{
+ 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 = (work_area->width % (rect->width + 1)) / 2;
+ rect->x = work_area->x + fluff;
+ fluff = (work_area->height % (rect->height + 1)) / 3;
+ rect->y = work_area->y + fluff;
+}
+
+static gboolean
+rect_fits_in_work_area (XRectangle *work_area,
+ XRectangle *rect)
+{
+ return ((rect->x >= work_area->x) &&
+ (rect->y >= work_area->y) &&
+ (rect->x + rect->width <= work_area->x + work_area->width) &&
+ (rect->y + rect->height <= work_area->y + work_area->height));
+}
+
+/* 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 gboolean
+find_first_fit (CompWindow *window,
+ GList *windows,
+ int x,
+ int y,
+ int *new_x,
+ int *new_y)
+{
+ /* 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.
+ */
+ int retval;
+ GList *below_sorted;
+ GList *right_sorted;
+ GList *tmp;
+ XRectangle rect;
+ XRectangle work_area;
+
+ retval = FALSE;
+
+ /* Below each window */
+ below_sorted = g_list_copy (windows);
+ below_sorted = g_list_sort (below_sorted, leftmost_cmp);
+ below_sorted = g_list_sort (below_sorted, topmost_cmp);
+
+ /* To the right of each window */
+ right_sorted = g_list_copy (windows);
+ right_sorted = g_list_sort (right_sorted, topmost_cmp);
+ right_sorted = g_list_sort (right_sorted, leftmost_cmp);
+
+ getOuterRectOfWindow (window, &rect);
+
+ work_area = window->screen->workArea;
+
+ center_tile_rect_in_area (&rect, &work_area);
+
+ if (rect_fits_in_work_area (&work_area, &rect) &&
+ !rectangle_overlaps_some_window (&rect, windows))
+ {
+ *new_x = rect.x + window->input.left;
+ *new_y = rect.y + window->input.top;
+
+ retval = TRUE;
+
+ goto out;
+ }
+
+ /* try below each window */
+ tmp = below_sorted;
+ while (tmp != NULL)
+ {
+ CompWindow *w = tmp->data;
+ XRectangle outer_rect;
+
+ getOuterRectOfWindow (w, &outer_rect);
+
+ rect.x = outer_rect.x;
+ rect.y = outer_rect.y + outer_rect.height;
+
+ if (rect_fits_in_work_area (&work_area, &rect) &&
+ !rectangle_overlaps_some_window (&rect, below_sorted))
+ {
+ *new_x = rect.x + window->input.left;
+ *new_y = rect.y + window->input.top;
+
+ retval = TRUE;
+
+ goto out;
+ }
+
+ tmp = tmp->next;
+ }
+
+ /* try to the right of each window */
+ tmp = right_sorted;
+ while (tmp != NULL)
+ {
+ CompWindow *w = tmp->data;
+ XRectangle outer_rect;
+
+ getOuterRectOfWindow (w, &outer_rect);
+
+ rect.x = outer_rect.x + outer_rect.width;
+ rect.y = outer_rect.y;
+
+ if (rect_fits_in_work_area (&work_area, &rect) &&
+ !rectangle_overlaps_some_window (&rect, right_sorted))
+ {
+ *new_x = rect.x + window->input.left;
+ *new_y = rect.y + window->input.top;
+
+ retval = TRUE;
+
+ goto out;
+ }
+
+ tmp = tmp->next;
+ }
+
+out:
+ g_list_free (below_sorted);
+ g_list_free (right_sorted);
+
+ return retval;
+}
+
+static void
+placeWindow (CompWindow *window,
+ int x,
+ int y,
+ int *new_x,
+ int *new_y)
+{
+ CompWindow *wi;
+ GList *windows;
+
+ PLACE_SCREEN (window->screen);
+
+ windows = NULL;
+
+ switch (window->type) {
+ case CompWindowTypeSplashMask:
+ case CompWindowTypeDialogMask:
+ case CompWindowTypeModalDialogMask:
+ case CompWindowTypeNormalMask:
+ /* Run placement algorithm on these. */
+ break;
+ case CompWindowTypeDockMask:
+ case CompWindowTypeDesktopMask:
+ case CompWindowTypeUtilMask:
+ case CompWindowTypeToolbarMask:
+ case CompWindowTypeMenuMask:
+ case CompWindowTypeFullscreenMask:
+ case CompWindowTypeUnknownMask:
+ /* Assume the app knows best how to place these, no placement
+ * algorithm ever (other than "leave them as-is")
+ */
+ goto done_no_constraints;
+ break;
+ }
+
+ if (ps->opt[PLACE_SCREEN_OPTION_WORKAROUND].value.b)
+ {
+ /* workarounds enabled */
+
+ if ((window->sizeHints.flags & PPosition) ||
+ (window->sizeHints.flags & USPosition))
+ {
+ avoid_being_obscured_as_second_modal_dialog (window, &x, &y);
+ goto done;
+ }
+ }
+ else
+ {
+ switch (window->type) {
+ case CompWindowTypeNormalMask:
+ /* Only accept USPosition on normal windows because the app is full
+ * of shit claiming the user set -geometry for a dialog or dock
+ */
+ if (window->sizeHints.flags & USPosition)
+ {
+ /* don't constrain with placement algorithm */
+ goto done;
+ }
+ break;
+ case CompWindowTypeSplashMask:
+ case CompWindowTypeDialogMask:
+ case CompWindowTypeModalDialogMask:
+ /* Ignore even USPosition on dialogs, splashscreen */
+ break;
+ case CompWindowTypeDockMask:
+ case CompWindowTypeDesktopMask:
+ case CompWindowTypeUtilMask:
+ case CompWindowTypeToolbarMask:
+ case CompWindowTypeMenuMask:
+ case CompWindowTypeFullscreenMask:
+ case CompWindowTypeUnknownMask:
+ /* Assume the app knows best how to place these. */
+ if (window->sizeHints.flags & PPosition)
+ {
+ goto done_no_constraints;
+ }
+ break;
+ }
+ }
+
+ if ((window->type == CompWindowTypeDialogMask ||
+ window->type == CompWindowTypeModalDialogMask) &&
+ window->transientFor != None)
+ {
+ /* Center horizontally, at top of parent vertically */
+
+ CompWindow *parent;
+
+ parent = findWindowAtDisplay (window->screen->display,
+ window->transientFor);
+ if (parent)
+ {
+ int w;
+ XRectangle area;
+
+ x = parent->attrib.x;
+ y = parent->attrib.y;
+
+ w = parent->width;
+
+ /* center of parent */
+ x = x + w / 2;
+
+ /* center of child over center of parent */
+ x -= window->width / 2;
+
+ /* "visually" center window over parent, leaving twice as
+ * much space below as on top.
+ */
+ y += (parent->height - window->height) / 3;
+
+ /* put top of child's frame, not top of child's client */
+ y += window->input.top;
+
+ /* clip to screen */
+ area = parent->screen->workArea;
+
+ if (x + window->width > area.x + area.width)
+ x = area.x + area.width - window->width;
+ if (y + window->height > area.y + area.height)
+ y = area.y + area.height - window->height;
+ if (x < area.x) x = area.x;
+ if (y < area.y) y = area.y;
+
+ avoid_being_obscured_as_second_modal_dialog (window, &x, &y);
+
+ goto done;
+ }
+ }
+
+ /* FIXME UTILITY with transient set should be stacked up
+ * on the sides of the parent window or something.
+ */
+ if (window->type == CompWindowTypeDialogMask ||
+ window->type == CompWindowTypeModalDialogMask ||
+ window->type == CompWindowTypeSplashMask)
+ {
+ /* Center on screen */
+ int w, h;
+
+ w = window->screen->width;
+ h = window->screen->height;
+
+ x = (w - window->width) / 2;
+ y = (h - window->height) / 2;
+
+ goto done_check_denied_focus;
+ }
+
+ /* 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 = window->screen->windows; wi; wi = wi->next)
+ {
+ if (WINDOW_INVISIBLE (wi))
+ continue;
+
+ if (wi->state & (CompWindowTypeDesktopMask |
+ CompWindowTypeDockMask |
+ CompWindowTypeFullscreenMask |
+ CompWindowTypeUnknownMask))
+ continue;
+
+ if (wi != window)
+ windows = g_list_prepend (windows, wi);
+ }
+
+ /* "Origin" placement algorithm */
+ x = y = 0;
+
+ if (find_first_fit (window, windows, x, y, &x, &y))
+ goto done_check_denied_focus;
+
+ /* if the window wasn't placed at the origin of screen,
+ * cascade it onto the current screen
+ */
+ find_next_cascade (window, windows, x, y, &x, &y);
+
+ /* 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 (window->has_maximize_func &&
+ window->decorated &&
+ !window->fullscreen)
+ {
+ XRectangle workarea;
+ XRectangle outer;
+
+ workarea = s->workArea;
+ getOuterRectOfWindow (window, &outer);
+
+ if (outer.width >= workarea.width &&
+ outer.height >= workarea.height)
+ {
+ window->maximize_after_placement = TRUE;
+ }
+ }
+ */
+
+done_check_denied_focus:
+ /* If the window is being denied focus and isn't a transient of the
+ * focus window, we do NOT want it to overlap with the focus window
+ * if at all possible. This is guaranteed to only be called if the
+ * focus_window is non-NULL, and we try to avoid that window.
+ */
+ if (0 /* window->denied_focus_and_not_transient */)
+ {
+ gboolean found_fit = FALSE;
+ CompWindow *focus_window;
+
+ focus_window =
+ findWindowAtDisplay (window->screen->display,
+ window->screen->display->activeWindow);
+ if (focus_window)
+ {
+ XRectangle wr, fwr, overlap;
+
+ getOuterRectOfWindow (window, &wr);
+ getOuterRectOfWindow (focus_window, &fwr);
+
+ /* No need to do anything if the window doesn't overlap at all */
+ found_fit = !rectangleIntersect (&wr, &fwr, &overlap);
+
+ /* Try to do a first fit again, this time only taking into
+ * account the focus window.
+ */
+ if (!found_fit)
+ {
+ GList *focus_window_list;
+
+ focus_window_list = g_list_prepend (NULL, focus_window);
+
+ /* Reset x and y ("origin" placement algorithm) */
+ x = 0;
+ y = 0;
+
+ found_fit = find_first_fit (window, focus_window_list,
+ x, y, &x, &y);
+
+ g_list_free (focus_window_list);
+ }
+ }
+
+ /* If that still didn't work, just place it where we can see as much
+ * as possible.
+ */
+ if (!found_fit)
+ find_most_freespace (window, focus_window, x, y, &x, &y);
+ }
+
+ g_list_free (windows);
+
+done:
+ if (x + window->width + window->input.right >
+ window->screen->workArea.x + window->screen->workArea.width)
+ x = window->screen->workArea.x + window->screen->workArea.width
+ - window->width - window->input.right;
+
+ if (x - window->input.left < window->screen->workArea.x)
+ x = window->screen->workArea.x + window->input.left;
+
+ if (y + window->height + window->input.bottom >
+ window->screen->workArea.y + window->screen->workArea.height)
+ y = window->screen->workArea.y + window->screen->workArea.height
+ - window->height - window->input.bottom;
+
+ if (y - window->input.top < window->screen->workArea.y)
+ y = window->screen->workArea.y + window->input.top;
+
+done_no_constraints:
+ *new_x = x;
+ *new_y = y;
+}
+
+static Bool
+placeDamageWindowRect (CompWindow *w,
+ Bool initial,
+ BoxPtr rect)
+{
+ Bool status;
+
+ PLACE_SCREEN (w->screen);
+
+ UNWRAP (ps, w->screen, damageWindowRect);
+ status = (*w->screen->damageWindowRect) (w, initial, rect);
+ WRAP (ps, w->screen, damageWindowRect, placeDamageWindowRect);
+
+ if (initial && !w->attrib.override_redirect && !w->placed)
+ {
+ int newX, newY;
+
+ placeWindow (w, w->attrib.x, w->attrib.y, &newX, &newY);
+
+ w->placed = TRUE;
+
+ if (newX != w->attrib.x || newY != w->attrib.y)
+ {
+ moveWindow (w, newX - w->attrib.x, newY - w->attrib.y, FALSE);
+ syncWindowPosition (w);
+ }
+ }
+
+ return status;
+}
+
+static Bool
+placeInitDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ PlaceDisplay *pd;
+
+ pd = malloc (sizeof (PlaceDisplay));
+ if (!pd)
+ return FALSE;
+
+ pd->screenPrivateIndex = allocateScreenPrivateIndex (d);
+ if (pd->screenPrivateIndex < 0)
+ {
+ free (pd);
+ return FALSE;
+ }
+
+ d->privates[displayPrivateIndex].ptr = pd;
+
+ return TRUE;
+}
+
+static void
+placeFiniDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ PLACE_DISPLAY (d);
+
+ freeScreenPrivateIndex (d, pd->screenPrivateIndex);
+
+ free (pd);
+}
+
+static Bool
+placeInitScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ PlaceScreen *ps;
+
+ PLACE_DISPLAY (s->display);
+
+ ps = malloc (sizeof (PlaceScreen));
+ if (!ps)
+ return FALSE;
+
+ placeScreenInitOptions (ps);
+
+ WRAP (ps, s, damageWindowRect, placeDamageWindowRect);
+
+ s->privates[pd->screenPrivateIndex].ptr = ps;
+
+ return TRUE;
+}
+
+static void
+placeFiniScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ PLACE_SCREEN (s);
+
+ UNWRAP (ps, s, damageWindowRect);
+
+ free (ps);
+}
+
+static Bool
+placeInit (CompPlugin *p)
+{
+ displayPrivateIndex = allocateDisplayPrivateIndex ();
+ if (displayPrivateIndex < 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+placeFini (CompPlugin *p)
+{
+ if (displayPrivateIndex >= 0)
+ freeDisplayPrivateIndex (displayPrivateIndex);
+}
+
+static CompPluginVTable placeVTable = {
+ "place",
+ "Place Windows",
+ "Place windows at appropriate positions when mapped",
+ placeInit,
+ placeFini,
+ placeInitDisplay,
+ placeFiniDisplay,
+ placeInitScreen,
+ placeFiniScreen,
+ 0, /* InitWindow */
+ 0, /* FiniWindow */
+ 0, /* GetDisplayOptions */
+ 0, /* SetDisplayOption */
+ placeGetScreenOptions,
+ placeSetScreenOption,
+ 0,
+ 0
+};
+
+CompPluginVTable *
+getCompPluginInfo (void)
+{
+ return &placeVTable;
+}
diff --git a/plugins/resize.c b/plugins/resize.c
new file mode 100644
index 00000000..b1bd8ef1
--- /dev/null
+++ b/plugins/resize.c
@@ -0,0 +1,691 @@
+/*
+ * 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.h>
+
+#define ResizeUpMask (1L << 0)
+#define ResizeDownMask (1L << 1)
+#define ResizeLeftMask (1L << 2)
+#define ResizeRightMask (1L << 3)
+
+#define RESIZE_INITIATE_BUTTON_DEFAULT Button3
+#define RESIZE_INITIATE_MODIFIERS_DEFAULT (CompPressMask | CompAltMask)
+
+#define RESIZE_TERMINATE_BUTTON_DEFAULT Button3
+#define RESIZE_TERMINATE_MODIFIERS_DEFAULT CompReleaseMask
+
+struct _ResizeKeys {
+ char *name;
+ int dx;
+ int dy;
+} rKeys[] = {
+ { "Left", -1, 0 },
+ { "Right", 1, 0 },
+ { "Up", 0, -1 },
+ { "Down", 0, 1 }
+};
+
+#define NUM_KEYS (sizeof (rKeys) / sizeof (rKeys[0]))
+
+#define MIN_KEY_WIDTH_INC 24
+#define MIN_KEY_HEIGHT_INC 24
+
+static int displayPrivateIndex;
+
+typedef struct _ResizeDisplay {
+ int screenPrivateIndex;
+ HandleEventProc handleEvent;
+
+ CompWindow *w;
+ int releaseButton;
+ unsigned int mask;
+ int width;
+ int height;
+ KeyCode key[NUM_KEYS];
+} ResizeDisplay;
+
+#define RESIZE_SCREEN_OPTION_INITIATE 0
+#define RESIZE_SCREEN_OPTION_TERMINATE 1
+#define RESIZE_SCREEN_OPTION_NUM 2
+
+typedef struct _ResizeScreen {
+ CompOption opt[RESIZE_SCREEN_OPTION_NUM];
+
+ int grabIndex;
+
+ Cursor leftCursor;
+ Cursor rightCursor;
+ Cursor upCursor;
+ Cursor upLeftCursor;
+ Cursor upRightCursor;
+ Cursor downCursor;
+ Cursor downLeftCursor;
+ Cursor downRightCursor;
+
+ int prevPointerX;
+ int prevPointerY;
+} ResizeScreen;
+
+#define GET_RESIZE_DISPLAY(d) \
+ ((ResizeDisplay *) (d)->privates[displayPrivateIndex].ptr)
+
+#define RESIZE_DISPLAY(d) \
+ ResizeDisplay *rd = GET_RESIZE_DISPLAY (d)
+
+#define GET_RESIZE_SCREEN(s, rd) \
+ ((ResizeScreen *) (s)->privates[(rd)->screenPrivateIndex].ptr)
+
+#define RESIZE_SCREEN(s) \
+ ResizeScreen *rs = GET_RESIZE_SCREEN (s, GET_RESIZE_DISPLAY (s->display))
+
+#define NUM_OPTIONS(s) (sizeof ((s)->opt) / sizeof (CompOption))
+
+static CompOption *
+resizeGetScreenOptions (CompScreen *screen,
+ int *count)
+{
+ RESIZE_SCREEN (screen);
+
+ *count = NUM_OPTIONS (rs);
+ return rs->opt;
+}
+
+static Bool
+resizeSetScreenOption (CompScreen *screen,
+ char *name,
+ CompOptionValue *value)
+{
+ CompOption *o;
+ int index;
+
+ RESIZE_SCREEN (screen);
+
+ o = compFindOption (rs->opt, NUM_OPTIONS (rs), name, &index);
+ if (!o)
+ return FALSE;
+
+ switch (index) {
+ case RESIZE_SCREEN_OPTION_INITIATE:
+ if (addScreenBinding (screen, &value->bind))
+ {
+ removeScreenBinding (screen, &o->value.bind);
+
+ if (compSetBindingOption (o, value))
+ return TRUE;
+ }
+ break;
+ case RESIZE_SCREEN_OPTION_TERMINATE:
+ if (compSetBindingOption (o, value))
+ return TRUE;
+ break;
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+static void
+resizeScreenInitOptions (ResizeScreen *rs,
+ Display *display)
+{
+ CompOption *o;
+
+ o = &rs->opt[RESIZE_SCREEN_OPTION_INITIATE];
+ o->name = "initiate";
+ o->shortDesc = "Initiate Window Resize";
+ o->longDesc = "Start moving window";
+ o->type = CompOptionTypeBinding;
+ o->value.bind.type = CompBindingTypeButton;
+ o->value.bind.u.button.modifiers = RESIZE_INITIATE_MODIFIERS_DEFAULT;
+ o->value.bind.u.button.button = RESIZE_INITIATE_BUTTON_DEFAULT;
+
+ o = &rs->opt[RESIZE_SCREEN_OPTION_TERMINATE];
+ o->name = "terminate";
+ o->shortDesc = "Terminate Window Resize";
+ o->longDesc = "Stop moving window";
+ o->type = CompOptionTypeBinding;
+ o->value.bind.type = CompBindingTypeButton;
+ o->value.bind.u.button.modifiers = RESIZE_TERMINATE_MODIFIERS_DEFAULT;
+ o->value.bind.u.button.button = RESIZE_TERMINATE_BUTTON_DEFAULT;
+}
+
+static void
+resizeInitiate (CompScreen *s,
+ Window window,
+ int x,
+ int y,
+ unsigned int state,
+ unsigned int mask,
+ int releaseButton)
+{
+ CompWindow *w;
+
+ RESIZE_DISPLAY (s->display);
+
+ /* some plugin has already grabbed the screen */
+ if (s->maxGrab)
+ return;
+
+ if (rd->w)
+ return;
+
+ w = findTopLevelWindowAtScreen (s, window);
+ if (w)
+ {
+ RESIZE_SCREEN (s);
+
+ if (w->type & (CompWindowTypeDesktopMask |
+ CompWindowTypeDockMask |
+ CompWindowTypeFullscreenMask))
+ return;
+
+ if (w->attrib.override_redirect)
+ return;
+
+ rd->w = w;
+ rd->mask = mask;
+ rd->width = w->attrib.width;
+ rd->height = w->attrib.height;
+
+ rs->prevPointerX = x;
+ rs->prevPointerY = y;
+
+ if (!rs->grabIndex)
+ {
+ Cursor cursor;
+
+ 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;
+ }
+
+ rs->grabIndex = pushScreenGrab (s, cursor);
+ }
+
+ if (rs->grabIndex)
+ {
+ rd->releaseButton = releaseButton;
+
+ (s->windowGrabNotify) (w, x, y, state,
+ CompWindowGrabResizeMask |
+ CompWindowGrabButtonMask);
+ }
+ }
+}
+
+static void
+resizeTerminate (CompDisplay *d)
+{
+ RESIZE_DISPLAY (d);
+
+ if (rd->w)
+ {
+ RESIZE_SCREEN (rd->w->screen);
+
+ if (rs->grabIndex)
+ {
+ removeScreenGrab (rd->w->screen, rs->grabIndex, NULL);
+ rs->grabIndex = 0;
+ }
+
+ (rd->w->screen->windowUngrabNotify) (rd->w);
+
+ syncWindowPosition (rd->w);
+
+ rd->w = 0;
+ rd->releaseButton = 0;
+ }
+}
+
+static void
+resizeUpdateWindowSize (CompDisplay *d)
+{
+ int width, height;
+
+ RESIZE_DISPLAY (d);
+
+ if (!rd->w)
+ return;
+
+ if (rd->w->state & CompWindowStateMaximizedVertMask)
+ rd->height = rd->w->attrib.height;
+
+ if (rd->w->state & CompWindowStateMaximizedHorzMask)
+ rd->width = rd->w->attrib.width;
+
+ if (rd->width == rd->w->attrib.width &&
+ rd->height == rd->w->attrib.height)
+ return;
+
+ if (rd->w->syncWait)
+ return;
+
+ if (constrainNewWindowSize (rd->w,
+ rd->width, rd->height,
+ &width, &height))
+ {
+ XWindowChanges xwc;
+
+ xwc.x = rd->w->attrib.x;
+ if (rd->mask & ResizeLeftMask)
+ xwc.x -= width - rd->w->attrib.width;
+
+ xwc.y = rd->w->attrib.y;
+ if (rd->mask & ResizeUpMask)
+ xwc.y -= height - rd->w->attrib.height;
+
+ xwc.width = width;
+ xwc.height = height;
+
+ sendSyncRequest (rd->w);
+
+ XConfigureWindow (d->display, rd->w->id,
+ CWX | CWY | CWWidth | CWHeight,
+ &xwc);
+ }
+}
+
+static void
+resizeHandleMotionEvent (CompScreen *s,
+ int xRoot,
+ int yRoot)
+{
+ RESIZE_SCREEN (s);
+
+ if (rs->grabIndex)
+ {
+ int pointerDx, pointerDy;
+
+ RESIZE_DISPLAY (s->display);
+
+ pointerDx = xRoot - rs->prevPointerX;
+ pointerDy = yRoot - rs->prevPointerY;
+ rs->prevPointerX = xRoot;
+ rs->prevPointerY = yRoot;
+
+ if (pointerDx || pointerDy)
+ {
+ if (rd->mask & ResizeLeftMask)
+ rd->width -= pointerDx;
+ else if (rd->mask & ResizeRightMask)
+ rd->width += pointerDx;
+
+ if (rd->mask & ResizeUpMask)
+ rd->height -= pointerDy;
+ else if (rd->mask & ResizeDownMask)
+ rd->height += pointerDy;
+
+ resizeUpdateWindowSize (s->display);
+ }
+ }
+}
+
+static void
+resizeHandleEvent (CompDisplay *d,
+ XEvent *event)
+{
+ CompScreen *s;
+
+ RESIZE_DISPLAY (d);
+
+ switch (event->type) {
+ case KeyPress:
+ case KeyRelease:
+ s = findScreenAtDisplay (d, event->xkey.root);
+ if (s)
+ {
+ RESIZE_SCREEN (s);
+
+ if (EV_KEY (&rs->opt[RESIZE_SCREEN_OPTION_INITIATE], event))
+ resizeInitiate (s,
+ event->xkey.window,
+ event->xkey.x_root,
+ event->xkey.y_root,
+ event->xkey.state,
+ ResizeDownMask | ResizeRightMask,
+ 0);
+
+ if (EV_KEY (&rs->opt[RESIZE_SCREEN_OPTION_TERMINATE], event) ||
+ (event->type == KeyPress &&
+ event->xkey.keycode == s->escapeKeyCode))
+ resizeTerminate (d);
+
+ if (rs->grabIndex && rd->w && event->type == KeyPress)
+ {
+ int i, widthInc, heightInc;
+
+ widthInc = rd->w->sizeHints.width_inc;
+ heightInc = rd->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 (event->xkey.keycode == rd->key[i])
+ {
+ XWarpPointer (d->display, None, None, 0, 0, 0, 0,
+ rKeys[i].dx * widthInc,
+ rKeys[i].dy * heightInc);
+ break;
+ }
+ }
+ }
+ }
+ break;
+ case ButtonPress:
+ case ButtonRelease:
+ s = findScreenAtDisplay (d, event->xbutton.root);
+ if (s)
+ {
+ RESIZE_SCREEN (s);
+
+ if (EV_BUTTON (&rs->opt[RESIZE_SCREEN_OPTION_INITIATE], event))
+ resizeInitiate (s,
+ event->xbutton.window,
+ event->xbutton.x_root,
+ event->xbutton.y_root,
+ event->xbutton.state,
+ ResizeDownMask | ResizeRightMask,
+ 0);
+
+ if (EV_BUTTON (&rs->opt[RESIZE_SCREEN_OPTION_TERMINATE], event))
+ resizeTerminate (d);
+
+ if (event->type == ButtonRelease &&
+ (rd->releaseButton == -1 ||
+ event->xbutton.button == rd->releaseButton))
+ resizeTerminate (d);
+ }
+ break;
+ case MotionNotify:
+ s = findScreenAtDisplay (d, event->xmotion.root);
+ if (s)
+ resizeHandleMotionEvent (s,
+ event->xmotion.x_root,
+ event->xmotion.y_root);
+ 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)
+ {
+ if (event->xclient.data.l[2] == WmMoveResizeSizeKeyboard)
+ {
+ int x, y;
+
+ x = w->attrib.x + w->width / 2;
+ y = w->attrib.y + w->height / 2;
+
+ XWarpPointer (d->display, None, w->screen->root,
+ 0, 0, 0, 0, x, y);
+
+ resizeInitiate (w->screen, event->xclient.window,
+ x, y, 0,
+ ResizeDownMask | ResizeRightMask,
+ 0);
+ }
+ else
+ {
+ static unsigned int mask[] = {
+ ResizeUpMask | ResizeLeftMask,
+ ResizeUpMask,
+ ResizeUpMask | ResizeRightMask,
+ ResizeRightMask,
+ ResizeDownMask | ResizeRightMask,
+ ResizeDownMask,
+ ResizeDownMask | ResizeLeftMask,
+ ResizeLeftMask,
+ };
+ unsigned int state;
+ Window root, child;
+ int xRoot, yRoot, i;
+
+ XQueryPointer (d->display, w->screen->root,
+ &root, &child, &xRoot, &yRoot,
+ &i, &i, &state);
+
+ /* TODO: not only button 1 */
+ if (state & Button1Mask)
+ {
+ resizeInitiate (w->screen, event->xclient.window,
+ event->xclient.data.l[0],
+ event->xclient.data.l[1],
+ state | CompPressMask,
+ mask[event->xclient.data.l[2]],
+ event->xclient.data.l[3] ?
+ event->xclient.data.l[3] : -1);
+
+ resizeHandleMotionEvent (w->screen, xRoot, yRoot);
+ }
+ }
+ }
+ }
+ }
+ break;
+ case DestroyNotify:
+ if (rd->w && rd->w->id == event->xdestroywindow.window)
+ resizeTerminate (d);
+ break;
+ case UnmapNotify:
+ if (rd->w && rd->w->id == event->xunmap.window)
+ resizeTerminate (d);
+ default:
+ if (event->type == d->syncEvent + XSyncAlarmNotify)
+ {
+ if (rd->w)
+ {
+ XSyncAlarmNotifyEvent *sa;
+
+ sa = (XSyncAlarmNotifyEvent *) event;
+
+ if (rd->w->syncAlarm == sa->alarm)
+ resizeUpdateWindowSize (d);
+ }
+ }
+ break;
+ }
+
+ UNWRAP (rd, d, handleEvent);
+ (*d->handleEvent) (d, event);
+ WRAP (rd, d, handleEvent, resizeHandleEvent);
+}
+
+static Bool
+resizeInitDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ ResizeDisplay *rd;
+ int i;
+
+ rd = malloc (sizeof (ResizeDisplay));
+ if (!rd)
+ return FALSE;
+
+ rd->screenPrivateIndex = allocateScreenPrivateIndex (d);
+ if (rd->screenPrivateIndex < 0)
+ {
+ free (rd);
+ return FALSE;
+ }
+
+ rd->w = 0;
+
+ rd->releaseButton = 0;
+
+ for (i = 0; i < NUM_KEYS; i++)
+ rd->key[i] = XKeysymToKeycode (d->display,
+ XStringToKeysym (rKeys[i].name));
+
+ WRAP (rd, d, handleEvent, resizeHandleEvent);
+
+ d->privates[displayPrivateIndex].ptr = rd;
+
+ return TRUE;
+}
+
+static void
+resizeFiniDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ RESIZE_DISPLAY (d);
+
+ freeScreenPrivateIndex (d, rd->screenPrivateIndex);
+
+ UNWRAP (rd, d, handleEvent);
+
+ 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->prevPointerX = 0;
+ rs->prevPointerY = 0;
+
+ resizeScreenInitOptions (rs, s->display->display);
+
+ 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);
+
+ addScreenBinding (s, &rs->opt[RESIZE_SCREEN_OPTION_INITIATE].value.bind);
+
+ s->privates[rd->screenPrivateIndex].ptr = rs;
+
+ return TRUE;
+}
+
+static void
+resizeFiniScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ RESIZE_SCREEN (s);
+
+ free (rs);
+}
+
+static Bool
+resizeInit (CompPlugin *p)
+{
+ displayPrivateIndex = allocateDisplayPrivateIndex ();
+ if (displayPrivateIndex < 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+resizeFini (CompPlugin *p)
+{
+ if (displayPrivateIndex >= 0)
+ freeDisplayPrivateIndex (displayPrivateIndex);
+}
+
+CompPluginVTable resizeVTable = {
+ "resize",
+ "Resize Window",
+ "Resize window",
+ resizeInit,
+ resizeFini,
+ resizeInitDisplay,
+ resizeFiniDisplay,
+ resizeInitScreen,
+ resizeFiniScreen,
+ 0, /* InitWindow */
+ 0, /* FiniWindow */
+ 0, /* GetDisplayOptions */
+ 0, /* SetDisplayOption */
+ resizeGetScreenOptions,
+ resizeSetScreenOption,
+ NULL,
+ 0
+};
+
+CompPluginVTable *
+getCompPluginInfo (void)
+{
+ return &resizeVTable;
+}
diff --git a/plugins/rotate.c b/plugins/rotate.c
new file mode 100644
index 00000000..9fa34082
--- /dev/null
+++ b/plugins/rotate.c
@@ -0,0 +1,1092 @@
+/*
+ * 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.h>
+
+#define ROTATE_POINTER_INVERT_Y_DEFAULT FALSE
+
+#define ROTATE_POINTER_SENSITIVITY_DEFAULT 1.0f
+#define ROTATE_POINTER_SENSITIVITY_MIN 0.01f
+#define ROTATE_POINTER_SENSITIVITY_MAX 100.0f
+#define ROTATE_POINTER_SENSITIVITY_PRECISION 0.01f
+
+#define ROTATE_POINTER_SENSITIVITY_FACTOR 0.05f
+
+#define ROTATE_ACCELERATION_DEFAULT 4.0f
+#define ROTATE_ACCELERATION_MIN 1.0f
+#define ROTATE_ACCELERATION_MAX 20.0f
+#define ROTATE_ACCELERATION_PRECISION 0.1f
+
+#define ROTATE_INITIATE_BUTTON_DEFAULT Button1
+#define ROTATE_INITIATE_MODIFIERS_DEFAULT \
+ (CompPressMask | ControlMask | CompAltMask)
+
+#define ROTATE_TERMINATE_BUTTON_DEFAULT Button1
+#define ROTATE_TERMINATE_MODIFIERS_DEFAULT CompReleaseMask
+
+#define ROTATE_LEFT_KEY_DEFAULT "Left"
+#define ROTATE_LEFT_MODIFIERS_DEFAULT \
+ (CompPressMask | ControlMask | CompAltMask)
+
+#define ROTATE_RIGHT_KEY_DEFAULT "Right"
+#define ROTATE_RIGHT_MODIFIERS_DEFAULT \
+ (CompPressMask | ControlMask | CompAltMask)
+
+#define ROTATE_LEFT_WINDOW_KEY_DEFAULT "Left"
+#define ROTATE_LEFT_WINDOW_MODIFIERS_DEFAULT \
+ (CompPressMask | ShiftMask | ControlMask | CompAltMask)
+
+#define ROTATE_RIGHT_WINDOW_KEY_DEFAULT "Right"
+#define ROTATE_RIGHT_WINDOW_MODIFIERS_DEFAULT \
+ (CompPressMask | ShiftMask | ControlMask | CompAltMask)
+
+#define ROTATE_SNAP_TOP_DEFAULT FALSE
+
+#define ROTATE_SPEED_DEFAULT 1.5f
+#define ROTATE_SPEED_MIN 0.1f
+#define ROTATE_SPEED_MAX 50.0f
+#define ROTATE_SPEED_PRECISION 0.1f
+
+#define ROTATE_TIMESTEP_DEFAULT 1.2f
+#define ROTATE_TIMESTEP_MIN 0.1f
+#define ROTATE_TIMESTEP_MAX 50.0f
+#define ROTATE_TIMESTEP_PRECISION 0.1f
+
+static int displayPrivateIndex;
+
+typedef struct _RotateDisplay {
+ int screenPrivateIndex;
+ HandleEventProc handleEvent;
+} 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_INITIATE 3
+#define ROTATE_SCREEN_OPTION_TERMINATE 4
+#define ROTATE_SCREEN_OPTION_LEFT 5
+#define ROTATE_SCREEN_OPTION_RIGHT 6
+#define ROTATE_SCREEN_OPTION_LEFT_WINDOW 7
+#define ROTATE_SCREEN_OPTION_RIGHT_WINDOW 8
+#define ROTATE_SCREEN_OPTION_SNAP_TOP 9
+#define ROTATE_SCREEN_OPTION_SPEED 10
+#define ROTATE_SCREEN_OPTION_TIMESTEP 11
+#define ROTATE_SCREEN_OPTION_NUM 12
+
+typedef struct _RotateScreen {
+ PreparePaintScreenProc preparePaintScreen;
+ DonePaintScreenProc donePaintScreen;
+ PaintScreenProc paintScreen;
+ SetScreenOptionForPluginProc setScreenOptionForPlugin;
+
+ CompOption opt[ROTATE_SCREEN_OPTION_NUM];
+
+ Bool pointerInvertY;
+ float pointerSensitivity;
+ Bool snapTop;
+ float acceleration;
+
+ float speed;
+ float timestep;
+
+ int grabIndex;
+
+ GLfloat xrot, xVelocity;
+ GLfloat yrot, yVelocity;
+
+ GLfloat baseXrot;
+
+ Bool moving;
+ GLfloat moveTo;
+
+ int invert;
+
+ Window moveWindow;
+ int moveWindowX;
+
+ int prevPointerX;
+ int prevPointerY;
+ XPoint savedPointer;
+ Bool grabbed;
+} RotateScreen;
+
+#define GET_ROTATE_DISPLAY(d) \
+ ((RotateDisplay *) (d)->privates[displayPrivateIndex].ptr)
+
+#define ROTATE_DISPLAY(d) \
+ RotateDisplay *rd = GET_ROTATE_DISPLAY (d)
+
+#define GET_ROTATE_SCREEN(s, rd) \
+ ((RotateScreen *) (s)->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 (CompScreen *screen,
+ int *count)
+{
+ ROTATE_SCREEN (screen);
+
+ *count = NUM_OPTIONS (rs);
+ return rs->opt;
+}
+
+static Bool
+rotateSetScreenOption (CompScreen *screen,
+ 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_INVERT_Y:
+ if (compSetBoolOption (o, value))
+ {
+ rs->pointerInvertY = o->value.b;
+ return TRUE;
+ }
+ break;
+ case ROTATE_SCREEN_OPTION_POINTER_SENSITIVITY:
+ if (compSetFloatOption (o, value))
+ {
+ rs->pointerSensitivity = o->value.f *
+ ROTATE_POINTER_SENSITIVITY_FACTOR;
+ return TRUE;
+ }
+ break;
+ case ROTATE_SCREEN_OPTION_ACCELERATION:
+ if (compSetFloatOption (o, value))
+ {
+ rs->acceleration = o->value.f;
+ return TRUE;
+ }
+ break;
+ case ROTATE_SCREEN_OPTION_INITIATE:
+ case ROTATE_SCREEN_OPTION_LEFT:
+ case ROTATE_SCREEN_OPTION_RIGHT:
+ case ROTATE_SCREEN_OPTION_LEFT_WINDOW:
+ case ROTATE_SCREEN_OPTION_RIGHT_WINDOW:
+ if (addScreenBinding (screen, &value->bind))
+ {
+ removeScreenBinding (screen, &o->value.bind);
+
+ if (compSetBindingOption (o, value))
+ return TRUE;
+ }
+ break;
+ case ROTATE_SCREEN_OPTION_TERMINATE:
+ if (compSetBindingOption (o, value))
+ return TRUE;
+ break;
+ case ROTATE_SCREEN_OPTION_SNAP_TOP:
+ if (compSetBoolOption (o, value))
+ {
+ rs->snapTop = o->value.b;
+ return TRUE;
+ }
+ break;
+ case ROTATE_SCREEN_OPTION_SPEED:
+ if (compSetFloatOption (o, value))
+ {
+ rs->speed = o->value.f;
+ return TRUE;
+ }
+ break;
+ case ROTATE_SCREEN_OPTION_TIMESTEP:
+ if (compSetFloatOption (o, value))
+ {
+ rs->timestep = o->value.f;
+ return TRUE;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+static void
+rotateScreenInitOptions (RotateScreen *rs,
+ Display *display)
+{
+ CompOption *o;
+
+ o = &rs->opt[ROTATE_SCREEN_OPTION_POINTER_INVERT_Y];
+ o->name = "invert_y";
+ o->shortDesc = "Pointer Invert Y";
+ o->longDesc = "Invert Y axis for pointer movement";
+ o->type = CompOptionTypeBool;
+ o->value.b = ROTATE_POINTER_INVERT_Y_DEFAULT;
+
+ o = &rs->opt[ROTATE_SCREEN_OPTION_POINTER_SENSITIVITY];
+ o->name = "sensitivity";
+ o->shortDesc = "Pointer Sensitivity";
+ o->longDesc = "Sensitivity of pointer movement";
+ o->type = CompOptionTypeFloat;
+ o->value.f = ROTATE_POINTER_SENSITIVITY_DEFAULT;
+ o->rest.f.min = ROTATE_POINTER_SENSITIVITY_MIN;
+ o->rest.f.max = ROTATE_POINTER_SENSITIVITY_MAX;
+ o->rest.f.precision = ROTATE_POINTER_SENSITIVITY_PRECISION;
+
+ o = &rs->opt[ROTATE_SCREEN_OPTION_ACCELERATION];
+ o->name = "acceleration";
+ o->shortDesc = "Acceleration";
+ o->longDesc = "Rotation Acceleration";
+ o->type = CompOptionTypeFloat;
+ o->value.f = ROTATE_ACCELERATION_DEFAULT;
+ o->rest.f.min = ROTATE_ACCELERATION_MIN;
+ o->rest.f.max = ROTATE_ACCELERATION_MAX;
+ o->rest.f.precision = ROTATE_ACCELERATION_PRECISION;
+
+ o = &rs->opt[ROTATE_SCREEN_OPTION_INITIATE];
+ o->name = "initiate";
+ o->shortDesc = "Initiate";
+ o->longDesc = "Start Rotation";
+ o->type = CompOptionTypeBinding;
+ o->value.bind.type = CompBindingTypeButton;
+ o->value.bind.u.button.modifiers = ROTATE_INITIATE_MODIFIERS_DEFAULT;
+ o->value.bind.u.button.button = ROTATE_INITIATE_BUTTON_DEFAULT;
+
+ o = &rs->opt[ROTATE_SCREEN_OPTION_TERMINATE];
+ o->name = "terminate";
+ o->shortDesc = "Terminate";
+ o->longDesc = "Stop Rotation";
+ o->type = CompOptionTypeBinding;
+ o->value.bind.type = CompBindingTypeButton;
+ o->value.bind.u.button.modifiers = ROTATE_TERMINATE_MODIFIERS_DEFAULT;
+ o->value.bind.u.button.button = ROTATE_TERMINATE_BUTTON_DEFAULT;
+
+ o = &rs->opt[ROTATE_SCREEN_OPTION_LEFT];
+ o->name = "rotate_left";
+ o->shortDesc = "Rotate Left";
+ o->longDesc = "Rotate left";
+ o->type = CompOptionTypeBinding;
+ o->value.bind.type = CompBindingTypeKey;
+ o->value.bind.u.key.modifiers = ROTATE_LEFT_MODIFIERS_DEFAULT;
+ o->value.bind.u.key.keycode =
+ XKeysymToKeycode (display,
+ XStringToKeysym (ROTATE_LEFT_KEY_DEFAULT));
+
+ o = &rs->opt[ROTATE_SCREEN_OPTION_RIGHT];
+ o->name = "rotate_right";
+ o->shortDesc = "Rotate Right";
+ o->longDesc = "Rotate right";
+ o->type = CompOptionTypeBinding;
+ o->value.bind.type = CompBindingTypeKey;
+ o->value.bind.u.key.modifiers = ROTATE_RIGHT_MODIFIERS_DEFAULT;
+ o->value.bind.u.key.keycode =
+ XKeysymToKeycode (display,
+ XStringToKeysym (ROTATE_RIGHT_KEY_DEFAULT));
+
+ o = &rs->opt[ROTATE_SCREEN_OPTION_LEFT_WINDOW];
+ o->name = "rotate_left_window";
+ o->shortDesc = "Rotate Left with Window";
+ o->longDesc = "Rotate left and bring active window "
+ "along";
+ o->type = CompOptionTypeBinding;
+ o->value.bind.type = CompBindingTypeKey;
+ o->value.bind.u.key.modifiers = ROTATE_LEFT_WINDOW_MODIFIERS_DEFAULT;
+ o->value.bind.u.key.keycode =
+ XKeysymToKeycode (display,
+ XStringToKeysym (ROTATE_LEFT_WINDOW_KEY_DEFAULT));
+
+ o = &rs->opt[ROTATE_SCREEN_OPTION_RIGHT_WINDOW];
+ o->name = "rotate_right_window";
+ o->shortDesc = "Rotate Right with Window";
+ o->longDesc = "Rotate right and bring active window "
+ "along";
+ o->type = CompOptionTypeBinding;
+ o->value.bind.type = CompBindingTypeKey;
+ o->value.bind.u.key.modifiers = ROTATE_RIGHT_WINDOW_MODIFIERS_DEFAULT;
+ o->value.bind.u.key.keycode =
+ XKeysymToKeycode (display,
+ XStringToKeysym (ROTATE_RIGHT_WINDOW_KEY_DEFAULT));
+
+ o = &rs->opt[ROTATE_SCREEN_OPTION_SNAP_TOP];
+ o->name = "snap_top";
+ o->shortDesc = "Snap To Top Face";
+ o->longDesc = "Snap Cube Rotation to Top Face";
+ o->type = CompOptionTypeBool;
+ o->value.b = ROTATE_SNAP_TOP_DEFAULT;
+
+ o = &rs->opt[ROTATE_SCREEN_OPTION_SPEED];
+ o->name = "speed";
+ o->shortDesc = "Speed";
+ o->longDesc = "Rotation Speed";
+ o->type = CompOptionTypeFloat;
+ o->value.f = ROTATE_SPEED_DEFAULT;
+ o->rest.f.min = ROTATE_SPEED_MIN;
+ o->rest.f.max = ROTATE_SPEED_MAX;
+ o->rest.f.precision = ROTATE_SPEED_PRECISION;
+
+ o = &rs->opt[ROTATE_SCREEN_OPTION_TIMESTEP];
+ o->name = "timestep";
+ o->shortDesc = "Timestep";
+ o->longDesc = "Rotation Timestep";
+ o->type = CompOptionTypeFloat;
+ o->value.f = ROTATE_TIMESTEP_DEFAULT;
+ o->rest.f.min = ROTATE_TIMESTEP_MIN;
+ o->rest.f.max = ROTATE_TIMESTEP_MAX;
+ o->rest.f.precision = ROTATE_TIMESTEP_PRECISION;
+}
+
+static int
+adjustVelocity (RotateScreen *rs,
+ int size)
+{
+ 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->acceleration;
+ amount = fabs (xrot);
+ if (amount < 10.0f)
+ amount = 10.0f;
+ else if (amount > 30.0f)
+ amount = 30.0f;
+
+ rs->xVelocity = (amount * rs->xVelocity + adjust) / (amount + 2.0f);
+
+ if (rs->snapTop && rs->yrot > 50.0f)
+ yrot = -(90.f - rs->yrot);
+ else
+ yrot = rs->yrot;
+
+ adjust = -yrot * 0.05f * rs->acceleration;
+ 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
+rotatePreparePaintScreen (CompScreen *s,
+ int msSinceLastPaint)
+{
+ ROTATE_SCREEN (s);
+
+ if (rs->grabIndex)
+ {
+ int steps;
+ float amount, chunk;
+
+ amount = msSinceLastPaint * 0.05f * rs->speed;
+ steps = amount / (0.5f * rs->timestep);
+ 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->size)
+ {
+ rs->baseXrot += 360.0f / s->size;
+ rs->xrot -= 360.0f / s->size;
+ }
+ else if (rs->xrot < 0.0f)
+ {
+ rs->baseXrot -= 360.0f / s->size;
+ rs->xrot += 360.0f / s->size;
+ }
+
+ if (rs->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->size))
+ {
+ 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->size * xrot / 360.0f) - 0.5f;
+ else
+ tx = (s->size * xrot / 360.0f) + 0.5f;
+
+ moveScreenViewport (s, tx, TRUE);
+
+ rs->xrot = 0.0f;
+ rs->yrot = 0.0f;
+ rs->baseXrot = rs->moveTo = 0.0f;
+ rs->moving = FALSE;
+
+ removeScreenGrab (s, rs->grabIndex, &rs->savedPointer);
+ rs->grabIndex = 0;
+
+ if (rs->moveWindow)
+ {
+ CompWindow *w;
+
+ w = findWindowAtScreen (s, rs->moveWindow);
+ if (w)
+ {
+ moveWindow (w, w->attrib.x - rs->moveWindowX, 0,
+ TRUE);
+ syncWindowPosition (w);
+ }
+ }
+ else
+ focusDefaultWindow (s->display);
+
+ rs->moveWindow = 0;
+ }
+ break;
+ }
+ }
+
+ if (rs->moveWindow)
+ {
+ CompWindow *w;
+
+ w = findWindowAtScreen (s, rs->moveWindow);
+ if (w)
+ {
+ float xrot = (s->size * (rs->baseXrot + rs->xrot)) / 360.0f;
+
+ moveWindowToViewportPosition (w,
+ rs->moveWindowX - xrot * s->width,
+ FALSE);
+ }
+ }
+ }
+
+ UNWRAP (rs, s, preparePaintScreen);
+ (*s->preparePaintScreen) (s, msSinceLastPaint);
+ WRAP (rs, s, preparePaintScreen, rotatePreparePaintScreen);
+}
+
+static void
+rotateDonePaintScreen (CompScreen *s)
+{
+ ROTATE_SCREEN (s);
+
+ if (rs->grabIndex)
+ {
+ if ((!rs->grabbed && !rs->snapTop) || rs->xVelocity || rs->yVelocity)
+ damageScreen (s);
+ }
+
+ UNWRAP (rs, s, donePaintScreen);
+ (*s->donePaintScreen) (s);
+ WRAP (rs, s, donePaintScreen, rotateDonePaintScreen);
+}
+
+static Bool
+rotatePaintScreen (CompScreen *s,
+ const ScreenPaintAttrib *sAttrib,
+ Region region,
+ unsigned int mask)
+{
+ Bool status;
+
+ ROTATE_SCREEN (s);
+
+ if (rs->grabIndex)
+ {
+ ScreenPaintAttrib sa = *sAttrib;
+
+ sa.xRotate += rs->baseXrot + rs->xrot;
+ sa.vRotate += rs->yrot;
+
+ mask &= ~PAINT_SCREEN_REGION_MASK;
+ mask |= PAINT_SCREEN_TRANSFORMED_MASK;
+
+ UNWRAP (rs, s, paintScreen);
+ status = (*s->paintScreen) (s, &sa, region, mask);
+ WRAP (rs, s, paintScreen, rotatePaintScreen);
+ }
+ else
+ {
+ UNWRAP (rs, s, paintScreen);
+ status = (*s->paintScreen) (s, sAttrib, region, mask);
+ WRAP (rs, s, paintScreen, rotatePaintScreen);
+ }
+
+ return status;
+}
+
+static void
+rotateInitiate (CompScreen *s,
+ int x,
+ int y)
+{
+ ROTATE_SCREEN (s);
+
+ rs->prevPointerX = x;
+ rs->prevPointerY = y;
+
+ rs->moving = FALSE;
+
+ /* some other plugin have already grabbed the screen */
+ if (s->maxGrab - rs->grabIndex)
+ return;
+
+ if (!rs->grabIndex)
+ {
+ rs->grabIndex = pushScreenGrab (s, s->invisibleCursor);
+ if (rs->grabIndex)
+ {
+ rs->savedPointer.x = rs->prevPointerX;
+ rs->savedPointer.y = rs->prevPointerY;
+ }
+ }
+
+ if (rs->grabIndex)
+ {
+ rs->grabbed = TRUE;
+ rs->snapTop = rs->opt[ROTATE_SCREEN_OPTION_SNAP_TOP].value.b;
+ }
+}
+
+static void
+rotate (CompScreen *s,
+ int x,
+ int y,
+ int direction)
+{
+ ROTATE_SCREEN (s);
+
+ if (!rs->grabIndex)
+ rotateInitiate (s, x, y);
+
+ if (rs->grabIndex)
+ {
+ rs->moving = TRUE;
+ rs->moveTo += (360.0f / s->size) * direction;
+ rs->grabbed = FALSE;
+
+ damageScreen (s);
+ }
+}
+
+static void
+rotateWithWindow (CompScreen *s,
+ int x,
+ int y,
+ int direction)
+{
+ CompWindow *w;
+
+ ROTATE_SCREEN (s);
+
+ if (!rs->grabIndex)
+ {
+ for (w = s->windows; w; w = w->next)
+ if (s->display->activeWindow == w->id)
+ break;
+
+ if (!w)
+ return;
+
+ if (w->type & (CompWindowTypeDesktopMask | CompWindowTypeDockMask))
+ return;
+
+ if (w->state & CompWindowStateStickyMask)
+ return;
+
+ rotateInitiate (s, x, y);
+
+ rs->moveWindow = w->id;
+ rs->moveWindowX = w->attrib.x;
+ }
+
+ if (rs->grabIndex)
+ {
+ rs->moving = TRUE;
+ rs->moveTo += (360.0f / s->size) * direction;
+ rs->grabbed = FALSE;
+
+ damageScreen (s);
+ }
+}
+
+static void
+rotateTerminate (CompScreen *s)
+{
+ ROTATE_SCREEN (s);
+
+ if (rs->grabIndex)
+ {
+ rs->grabbed = FALSE;
+ damageScreen (s);
+ }
+}
+
+static void
+rotateHandleEvent (CompDisplay *d,
+ XEvent *event)
+{
+ CompScreen *s;
+
+ ROTATE_DISPLAY (d);
+
+ switch (event->type) {
+ case KeyPress:
+ case KeyRelease:
+ s = findScreenAtDisplay (d, event->xkey.root);
+ if (s)
+ {
+ ROTATE_SCREEN (s);
+
+ if (EV_KEY (&rs->opt[ROTATE_SCREEN_OPTION_INITIATE], event))
+ rotateInitiate (s, event->xkey.x_root, event->xkey.y_root);
+
+ if (EV_KEY (&rs->opt[ROTATE_SCREEN_OPTION_LEFT_WINDOW], event))
+ rotateWithWindow (s, event->xkey.x_root, event->xkey.y_root, -1);
+ else if (EV_KEY (&rs->opt[ROTATE_SCREEN_OPTION_LEFT], event))
+ rotate (s, event->xkey.x_root, event->xkey.y_root, -1);
+
+ if (EV_KEY (&rs->opt[ROTATE_SCREEN_OPTION_RIGHT_WINDOW], event))
+ rotateWithWindow (s, event->xkey.x_root, event->xkey.y_root, 1);
+ else if (EV_KEY (&rs->opt[ROTATE_SCREEN_OPTION_RIGHT], event))
+ rotate (s, event->xkey.x_root, event->xkey.y_root, 1);
+
+ if (EV_KEY (&rs->opt[ROTATE_SCREEN_OPTION_TERMINATE], event))
+ rotateTerminate (s);
+
+ if (event->type == KeyPress &&
+ event->xkey.keycode == s->escapeKeyCode)
+ {
+ rs->snapTop = FALSE;
+ rotateTerminate (s);
+ }
+ }
+ break;
+ case ButtonPress:
+ case ButtonRelease:
+ s = findScreenAtDisplay (d, event->xbutton.root);
+ if (s)
+ {
+ ROTATE_SCREEN (s);
+
+ if (EV_BUTTON (&rs->opt[ROTATE_SCREEN_OPTION_INITIATE], event))
+ rotateInitiate (s,
+ event->xbutton.x_root,
+ event->xbutton.y_root);
+
+ if (EV_BUTTON (&rs->opt[ROTATE_SCREEN_OPTION_LEFT_WINDOW], event))
+ rotateWithWindow (s, event->xbutton.x_root,
+ event->xbutton.y_root, -1);
+ else if (EV_BUTTON (&rs->opt[ROTATE_SCREEN_OPTION_LEFT], event))
+ rotate (s, event->xbutton.x_root, event->xbutton.y_root, -1);
+
+ if (EV_BUTTON (&rs->opt[ROTATE_SCREEN_OPTION_RIGHT_WINDOW], event))
+ rotateWithWindow (s, event->xbutton.x_root,
+ event->xbutton.y_root, 1);
+ else if (EV_BUTTON (&rs->opt[ROTATE_SCREEN_OPTION_RIGHT], event))
+ rotate (s, event->xbutton.x_root, event->xbutton.y_root, 1);
+
+ if (EV_BUTTON (&rs->opt[ROTATE_SCREEN_OPTION_TERMINATE], event))
+ rotateTerminate (s);
+ }
+ break;
+ case MotionNotify:
+ s = findScreenAtDisplay (d, event->xmotion.root);
+ if (s)
+ {
+ ROTATE_SCREEN (s);
+
+ if (rs->grabIndex && rs->grabbed)
+ {
+ GLfloat pointerDx;
+ GLfloat pointerDy;
+
+ pointerDx = event->xmotion.x_root - rs->prevPointerX;
+ pointerDy = event->xmotion.y_root - rs->prevPointerY;
+ rs->prevPointerX = event->xmotion.x_root;
+ rs->prevPointerY = event->xmotion.y_root;
+
+ 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)
+ {
+ rs->prevPointerX = s->width / 2;
+ rs->prevPointerY = s->height / 2;
+
+ XWarpPointer (d->display, None, s->root, 0, 0, 0, 0,
+ rs->prevPointerX, rs->prevPointerY);
+ }
+
+ if (rs->pointerInvertY)
+ pointerDy = -pointerDy;
+
+ rs->xVelocity += pointerDx * rs->pointerSensitivity * rs->invert;
+ rs->yVelocity += pointerDy * rs->pointerSensitivity;
+
+ damageScreen (s);
+
+ return;
+ }
+ }
+ break;
+ case ClientMessage:
+ if (event->xclient.message_type == d->winActiveAtom)
+ {
+ CompWindow *w;
+
+ w = findWindowAtDisplay (d, event->xclient.window);
+ if (w)
+ {
+ s = w->screen;
+
+ if (w->attrib.x >= s->width || w->attrib.x + w->width <= 0)
+ {
+ Window win;
+ int i, x, y, dx;
+ unsigned int ui;
+
+ XQueryPointer (d->display, s->root,
+ &win, &win, &x, &y, &i, &i, &ui);
+
+ if (w->attrib.x >= s->width)
+ dx = w->attrib.x / s->width;
+ else
+ dx = ((w->attrib.x + w->width) / s->width) - 1;
+
+ if (dx > (s->size + 1) / 2)
+ dx -= s->size;
+ else if (dx < -(s->size + 1) / 2)
+ dx += s->size;
+
+ rotate (s, x, y, dx);
+ }
+ }
+ }
+ else if (event->xclient.message_type == d->desktopViewportAtom)
+ {
+ s = findScreenAtDisplay (d, event->xclient.window);
+ if (s)
+ {
+ int dx;
+
+ dx = event->xclient.data.l[0] / s->width - s->x;
+ if (dx)
+ {
+ Window win;
+ int i, x, y;
+ unsigned int ui;
+
+ XQueryPointer (d->display, s->root,
+ &win, &win, &x, &y, &i, &i, &ui);
+
+ if (dx > (s->size + 1) / 2)
+ dx -= s->size;
+ else if (dx < -(s->size + 1) / 2)
+ dx += s->size;
+
+ rotate (s, x, y, dx);
+ }
+ }
+ }
+ default:
+ break;
+ }
+
+ UNWRAP (rd, d, handleEvent);
+ (*d->handleEvent) (d, event);
+ WRAP (rd, d, handleEvent, rotateHandleEvent);
+}
+
+static void
+rotateUpdateCubeOptions (CompScreen *s)
+{
+ CompPlugin *p;
+
+ ROTATE_SCREEN (s);
+
+ p = findActivePlugin ("cube");
+ if (p && p->vTable->getScreenOptions)
+ {
+ CompOption *options, *option;
+ int nOptions;
+
+ options = (*p->vTable->getScreenOptions) (s, &nOptions);
+ option = compFindOption (options, nOptions, "in", 0);
+ if (option)
+ rs->invert = option->value.b ? -1 : 1;
+ }
+}
+
+static Bool
+rotateSetScreenOptionForPlugin (CompScreen *s,
+ char *plugin,
+ char *name,
+ CompOptionValue *value)
+{
+ Bool status;
+
+ ROTATE_SCREEN (s);
+
+ UNWRAP (rs, s, setScreenOptionForPlugin);
+ status = (*s->setScreenOptionForPlugin) (s, plugin, name, value);
+ WRAP (rs, s, setScreenOptionForPlugin, rotateSetScreenOptionForPlugin);
+
+ if (status && strcmp (plugin, "cube") == 0 && strcmp (name, "in") == 0)
+ rotateUpdateCubeOptions (s);
+
+ return status;
+}
+
+static Bool
+rotateInitDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ RotateDisplay *rd;
+
+ rd = malloc (sizeof (RotateDisplay));
+ if (!rd)
+ return FALSE;
+
+ rd->screenPrivateIndex = allocateScreenPrivateIndex (d);
+ if (rd->screenPrivateIndex < 0)
+ {
+ free (rd);
+ return FALSE;
+ }
+
+ WRAP (rd, d, handleEvent, rotateHandleEvent);
+
+ d->privates[displayPrivateIndex].ptr = rd;
+
+ return TRUE;
+}
+
+static void
+rotateFiniDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ ROTATE_DISPLAY (d);
+
+ freeScreenPrivateIndex (d, rd->screenPrivateIndex);
+
+ UNWRAP (rd, d, handleEvent);
+
+ free (rd);
+}
+
+static Bool
+rotateInitScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ RotateScreen *rs;
+
+ ROTATE_DISPLAY (s->display);
+
+ rs = malloc (sizeof (RotateScreen));
+ if (!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->prevPointerX = 0;
+ rs->prevPointerY = 0;
+
+ rs->grabbed = FALSE;
+ rs->snapTop = FALSE;
+
+ rs->acceleration = ROTATE_ACCELERATION_DEFAULT;
+
+ rs->pointerInvertY = ROTATE_POINTER_INVERT_Y_DEFAULT;
+ rs->pointerSensitivity = ROTATE_POINTER_SENSITIVITY_DEFAULT *
+ ROTATE_POINTER_SENSITIVITY_FACTOR;
+
+ rs->speed = ROTATE_SPEED_DEFAULT;
+ rs->timestep = ROTATE_TIMESTEP_DEFAULT;
+
+ rotateScreenInitOptions (rs, s->display->display);
+
+ addScreenBinding (s, &rs->opt[ROTATE_SCREEN_OPTION_INITIATE].value.bind);
+ addScreenBinding (s, &rs->opt[ROTATE_SCREEN_OPTION_LEFT].value.bind);
+ addScreenBinding (s, &rs->opt[ROTATE_SCREEN_OPTION_RIGHT].value.bind);
+ addScreenBinding (s,
+ &rs->opt[ROTATE_SCREEN_OPTION_LEFT_WINDOW].value.bind);
+ addScreenBinding (s,
+ &rs->opt[ROTATE_SCREEN_OPTION_RIGHT_WINDOW].value.bind);
+
+ WRAP (rs, s, preparePaintScreen, rotatePreparePaintScreen);
+ WRAP (rs, s, donePaintScreen, rotateDonePaintScreen);
+ WRAP (rs, s, paintScreen, rotatePaintScreen);
+ WRAP (rs, s, setScreenOptionForPlugin, rotateSetScreenOptionForPlugin);
+
+ s->privates[rd->screenPrivateIndex].ptr = rs;
+
+ rotateUpdateCubeOptions (s);
+
+ return TRUE;
+}
+
+static void
+rotateFiniScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ ROTATE_SCREEN (s);
+
+ UNWRAP (rs, s, preparePaintScreen);
+ UNWRAP (rs, s, donePaintScreen);
+ UNWRAP (rs, s, paintScreen);
+ UNWRAP (rs, s, setScreenOptionForPlugin);
+
+ free (rs);
+}
+
+static Bool
+rotateInit (CompPlugin *p)
+{
+ displayPrivateIndex = allocateDisplayPrivateIndex ();
+ if (displayPrivateIndex < 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+rotateFini (CompPlugin *p)
+{
+ if (displayPrivateIndex >= 0)
+ freeDisplayPrivateIndex (displayPrivateIndex);
+}
+
+CompPluginDep rotateDeps[] = {
+ { CompPluginRuleAfter, "cube" }
+};
+
+CompPluginVTable rotateVTable = {
+ "rotate",
+ "Rotate Cube",
+ "Rotate desktop cube",
+ rotateInit,
+ rotateFini,
+ rotateInitDisplay,
+ rotateFiniDisplay,
+ rotateInitScreen,
+ rotateFiniScreen,
+ 0, /* InitWindow */
+ 0, /* FiniWindow */
+ 0, /* GetDisplayOptions */
+ 0, /* SetDisplayOption */
+ rotateGetScreenOptions,
+ rotateSetScreenOption,
+ rotateDeps,
+ sizeof (rotateDeps) / sizeof (rotateDeps[0])
+};
+
+CompPluginVTable *
+getCompPluginInfo (void)
+{
+ return &rotateVTable;
+}
diff --git a/plugins/scale.c b/plugins/scale.c
new file mode 100644
index 00000000..cfca10ef
--- /dev/null
+++ b/plugins/scale.c
@@ -0,0 +1,1263 @@
+/*
+ * 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.h>
+
+#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)
+
+#define SCALE_SPACING_DEFAULT 25
+#define SCALE_SPACING_MIN 0
+#define SCALE_SPACING_MAX 250
+
+#define SCALE_SLOPPY_FOCUS_DEFAULT FALSE
+
+#define SCALE_INITIATE_KEY_DEFAULT "F12"
+#define SCALE_INITIATE_MODIFIERS_DEFAULT CompPressMask
+
+#define SCALE_TERMINATE_KEY_DEFAULT "F12"
+#define SCALE_TERMINATE_MODIFIERS_DEFAULT CompPressMask
+
+#define SCALE_NEXT_WINDOW_KEY_DEFAULT "Right"
+#define SCALE_NEXT_WINDOW_MODIFIERS_DEFAULT CompPressMask
+
+#define SCALE_SPEED_DEFAULT 1.5f
+#define SCALE_SPEED_MIN 0.1f
+#define SCALE_SPEED_MAX 50.0f
+#define SCALE_SPEED_PRECISION 0.1f
+
+#define SCALE_TIMESTEP_DEFAULT 1.2f
+#define SCALE_TIMESTEP_MIN 0.1f
+#define SCALE_TIMESTEP_MAX 50.0f
+#define SCALE_TIMESTEP_PRECISION 0.1f
+
+#define SCALE_STATE_NONE 0
+#define SCALE_STATE_OUT 1
+#define SCALE_STATE_WAIT 2
+#define SCALE_STATE_IN 3
+
+static char *winType[] = {
+ "Toolbar",
+ "Utility",
+ "Dialog",
+ "Normal"
+};
+#define N_WIN_TYPE (sizeof (winType) / sizeof (winType[0]))
+
+static int displayPrivateIndex;
+
+typedef struct _ScaleSlot {
+ int x1, y1, x2, y2;
+ int line;
+} ScaleSlot;
+
+typedef struct _ScaleDisplay {
+ int screenPrivateIndex;
+ HandleEventProc handleEvent;
+
+ unsigned int lastActiveNum;
+} ScaleDisplay;
+
+#define SCALE_SCREEN_OPTION_SPACING 0
+#define SCALE_SCREEN_OPTION_SLOPPY_FOCUS 1
+#define SCALE_SCREEN_OPTION_INITIATE 2
+#define SCALE_SCREEN_OPTION_TERMINATE 3
+#define SCALE_SCREEN_OPTION_NEXT_WINDOW 4
+#define SCALE_SCREEN_OPTION_SPEED 5
+#define SCALE_SCREEN_OPTION_TIMESTEP 6
+#define SCALE_SCREEN_OPTION_WINDOW_TYPE 7
+#define SCALE_SCREEN_OPTION_NUM 8
+
+typedef struct _ScaleScreen {
+ int windowPrivateIndex;
+
+ PreparePaintScreenProc preparePaintScreen;
+ DonePaintScreenProc donePaintScreen;
+ PaintScreenProc paintScreen;
+ PaintWindowProc paintWindow;
+ DamageWindowRectProc damageWindowRect;
+
+ CompOption opt[SCALE_SCREEN_OPTION_NUM];
+
+ int spacing;
+
+ float speed;
+ float timestep;
+
+ unsigned int wMask;
+
+ int grabIndex;
+
+ int state;
+ int moreAdjust;
+
+ Cursor cursor;
+
+ ScaleSlot *slots;
+ int slotsSize;
+ int nSlots;
+
+ int *line;
+ int lineSize;
+ int nLine;
+
+ /* only used for sorting */
+ CompWindow **windows;
+ int windowsSize;
+ int nWindows;
+
+ GLfloat scale;
+} ScaleScreen;
+
+typedef struct _ScaleWindow {
+ ScaleSlot *slot;
+
+ GLfloat xVelocity, yVelocity, scaleVelocity;
+ GLfloat scale;
+ GLfloat tx, ty;
+ Bool adjust;
+} ScaleWindow;
+
+
+#define GET_SCALE_DISPLAY(d) \
+ ((ScaleDisplay *) (d)->privates[displayPrivateIndex].ptr)
+
+#define SCALE_DISPLAY(d) \
+ ScaleDisplay *sd = GET_SCALE_DISPLAY (d)
+
+#define GET_SCALE_SCREEN(s, sd) \
+ ((ScaleScreen *) (s)->privates[(sd)->screenPrivateIndex].ptr)
+
+#define SCALE_SCREEN(s) \
+ ScaleScreen *ss = GET_SCALE_SCREEN (s, GET_SCALE_DISPLAY (s->display))
+
+#define GET_SCALE_WINDOW(w, ss) \
+ ((ScaleWindow *) (w)->privates[(ss)->windowPrivateIndex].ptr)
+
+#define SCALE_WINDOW(w) \
+ ScaleWindow *sw = GET_SCALE_WINDOW (w, \
+ GET_SCALE_SCREEN (w->screen, \
+ GET_SCALE_DISPLAY (w->screen->display)))
+
+#define NUM_OPTIONS(s) (sizeof ((s)->opt) / sizeof (CompOption))
+
+static CompOption *
+scaleGetScreenOptions (CompScreen *screen,
+ int *count)
+{
+ SCALE_SCREEN (screen);
+
+ *count = NUM_OPTIONS (ss);
+ return ss->opt;
+}
+
+static Bool
+scaleSetScreenOption (CompScreen *screen,
+ 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_SPACING:
+ if (compSetIntOption (o, value))
+ {
+ ss->spacing = o->value.i;
+ return TRUE;
+ }
+ break;
+ case SCALE_SCREEN_OPTION_SLOPPY_FOCUS:
+ if (compSetBoolOption (o, value))
+ return TRUE;
+ break;
+ case SCALE_SCREEN_OPTION_INITIATE:
+ if (addScreenBinding (screen, &value->bind))
+ {
+ removeScreenBinding (screen, &o->value.bind);
+
+ if (compSetBindingOption (o, value))
+ return TRUE;
+ }
+ break;
+ case SCALE_SCREEN_OPTION_TERMINATE:
+ case SCALE_SCREEN_OPTION_NEXT_WINDOW:
+ if (compSetBindingOption (o, value))
+ return TRUE;
+ break;
+ case SCALE_SCREEN_OPTION_SPEED:
+ if (compSetFloatOption (o, value))
+ {
+ ss->speed = o->value.f;
+ return TRUE;
+ }
+ break;
+ case SCALE_SCREEN_OPTION_TIMESTEP:
+ if (compSetFloatOption (o, value))
+ {
+ ss->timestep = o->value.f;
+ return TRUE;
+ }
+ break;
+ case SCALE_SCREEN_OPTION_WINDOW_TYPE:
+ if (compSetOptionList (o, value))
+ {
+ ss->wMask = compWindowTypeMaskFromStringList (&o->value);
+ return TRUE;
+ }
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+static void
+scaleScreenInitOptions (ScaleScreen *ss,
+ Display *display)
+{
+ CompOption *o;
+ int i;
+
+ o = &ss->opt[SCALE_SCREEN_OPTION_SPACING];
+ o->name = "spacing";
+ o->shortDesc = "Spacing";
+ o->longDesc = "Space between windows";
+ o->type = CompOptionTypeInt;
+ o->value.i = SCALE_SPACING_DEFAULT;
+ o->rest.i.min = SCALE_SPACING_MIN;
+ o->rest.i.max = SCALE_SPACING_MAX;
+
+ o = &ss->opt[SCALE_SCREEN_OPTION_SLOPPY_FOCUS];
+ o->name = "sloppy_focus";
+ o->shortDesc = "Sloppy Focus";
+ o->longDesc = "Focus window when mouse moves over them";
+ o->type = CompOptionTypeBool;
+ o->value.b = SCALE_SLOPPY_FOCUS_DEFAULT;
+
+ o = &ss->opt[SCALE_SCREEN_OPTION_INITIATE];
+ o->name = "initiate";
+ o->shortDesc = "Initiate";
+ o->longDesc = "Layout and start transforming windows";
+ o->type = CompOptionTypeBinding;
+ o->value.bind.type = CompBindingTypeKey;
+ o->value.bind.u.key.modifiers = SCALE_INITIATE_MODIFIERS_DEFAULT;
+ o->value.bind.u.key.keycode =
+ XKeysymToKeycode (display,
+ XStringToKeysym (SCALE_INITIATE_KEY_DEFAULT));
+
+ o = &ss->opt[SCALE_SCREEN_OPTION_TERMINATE];
+ o->name = "terminate";
+ o->shortDesc = "Terminate";
+ o->longDesc = "Return from scale view";
+ o->type = CompOptionTypeBinding;
+ o->value.bind.type = CompBindingTypeKey;
+ o->value.bind.u.key.modifiers = SCALE_TERMINATE_MODIFIERS_DEFAULT;
+ o->value.bind.u.key.keycode =
+ XKeysymToKeycode (display,
+ XStringToKeysym (SCALE_TERMINATE_KEY_DEFAULT));
+
+ o = &ss->opt[SCALE_SCREEN_OPTION_NEXT_WINDOW];
+ o->name = "next_window";
+ o->shortDesc = "Next Window";
+ o->longDesc = "Focus next window";
+ o->type = CompOptionTypeBinding;
+ o->value.bind.type = CompBindingTypeKey;
+ o->value.bind.u.key.modifiers = SCALE_NEXT_WINDOW_MODIFIERS_DEFAULT;
+ o->value.bind.u.key.keycode =
+ XKeysymToKeycode (display,
+ XStringToKeysym (SCALE_NEXT_WINDOW_KEY_DEFAULT));
+
+ o = &ss->opt[SCALE_SCREEN_OPTION_SPEED];
+ o->name = "speed";
+ o->shortDesc = "Speed";
+ o->longDesc = "Scale speed";
+ o->type = CompOptionTypeFloat;
+ o->value.f = SCALE_SPEED_DEFAULT;
+ o->rest.f.min = SCALE_SPEED_MIN;
+ o->rest.f.max = SCALE_SPEED_MAX;
+ o->rest.f.precision = SCALE_SPEED_PRECISION;
+
+ o = &ss->opt[SCALE_SCREEN_OPTION_TIMESTEP];
+ o->name = "timestep";
+ o->shortDesc = "Timestep";
+ o->longDesc = "Scale timestep";
+ o->type = CompOptionTypeFloat;
+ o->value.f = SCALE_TIMESTEP_DEFAULT;
+ o->rest.f.min = SCALE_TIMESTEP_MIN;
+ o->rest.f.max = SCALE_TIMESTEP_MAX;
+ o->rest.f.precision = SCALE_TIMESTEP_PRECISION;
+
+ o = &ss->opt[SCALE_SCREEN_OPTION_WINDOW_TYPE];
+ o->name = "window_types";
+ o->shortDesc = "Window Types";
+ o->longDesc = "Window types that should scaled in scale mode";
+ o->type = CompOptionTypeList;
+ o->value.list.type = CompOptionTypeString;
+ o->value.list.nValue = N_WIN_TYPE;
+ o->value.list.value = malloc (sizeof (CompOptionValue) * N_WIN_TYPE);
+ for (i = 0; i < N_WIN_TYPE; i++)
+ o->value.list.value[i].s = strdup (winType[i]);
+ o->rest.s.string = windowTypeString;
+ o->rest.s.nString = nWindowTypeString;
+
+ ss->wMask = compWindowTypeMaskFromStringList (&o->value);
+}
+
+static Bool
+scalePaintWindow (CompWindow *w,
+ const WindowPaintAttrib *attrib,
+ Region region,
+ unsigned int mask)
+{
+ CompScreen *s = w->screen;
+ Bool status;
+
+ SCALE_SCREEN (s);
+ SCALE_WINDOW (w);
+
+ if (ss->grabIndex && (sw->adjust || sw->slot))
+ mask |= PAINT_WINDOW_TRANSFORMED_MASK;
+
+ UNWRAP (ss, s, paintWindow);
+ status = (*s->paintWindow) (w, attrib, region, mask);
+ WRAP (ss, s, paintWindow, scalePaintWindow);
+
+ return status;
+}
+
+static Bool
+isScaleWin (CompWindow *w)
+{
+ SCALE_SCREEN (w->screen);
+
+ if (!(*w->screen->focusWindow) (w))
+ return FALSE;
+
+ if (!(ss->wMask & w->type))
+ return FALSE;
+
+ if (w->state & CompWindowStateSkipPagerMask)
+ return FALSE;
+
+ return TRUE;
+}
+
+static int
+compareWindows (const void *elem1,
+ const void *elem2)
+{
+ CompWindow *w1 = *((CompWindow **) elem1);
+ CompWindow *w2 = *((CompWindow **) elem2);
+
+ return w2->activeNum - w1->activeNum;
+}
+
+/* TODO: Place window thumbnails at smarter positions */
+static Bool
+layoutThumbs (CompScreen *s)
+{
+ CompWindow *w;
+ int i, j, y2;
+ int cx, cy;
+ int lineLength, itemsPerLine;
+ float scaleW, scaleH;
+ int totalWidth, totalHeight;
+
+ SCALE_SCREEN (s);
+
+ cx = cy = ss->nWindows = 0;
+
+ for (w = s->windows; w; w = w->next)
+ {
+ 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;
+
+ qsort (ss->windows, ss->nWindows, sizeof (CompWindow *), compareWindows);
+
+ itemsPerLine = (sqrt (ss->nWindows) * s->width) / s->height;
+ if (itemsPerLine < 1)
+ itemsPerLine = 1;
+
+ if (ss->lineSize <= ss->nWindows / itemsPerLine + 1)
+ {
+ ss->line = realloc (ss->line, sizeof (int) *
+ (ss->nWindows / itemsPerLine + 2));
+ if (!ss->line)
+ return FALSE;
+
+ ss->lineSize = ss->nWindows / itemsPerLine + 2;
+ }
+
+ totalWidth = totalHeight = 0;
+
+ ss->line[0] = 0;
+ ss->nLine = 1;
+ lineLength = itemsPerLine;
+
+ if (ss->slotsSize <= ss->nWindows)
+ {
+ ss->slots = realloc (ss->slots, sizeof (ScaleSlot) *
+ (ss->nWindows + 1));
+ if (!ss->slots)
+ return FALSE;
+
+ ss->slotsSize = ss->nWindows + 1;
+ }
+ ss->nSlots = 0;
+
+ for (i = 0; i < ss->nWindows; i++)
+ {
+ SCALE_WINDOW (ss->windows[i]);
+
+ w = ss->windows[i];
+
+ /* find a good place between other elements */
+ for (j = 0; j < ss->nSlots; j++)
+ {
+ y2 = ss->slots[j].y2 + ss->spacing + WIN_H (w);
+ if (w->width < ss->slots[j].x2 - ss->slots[j].x1 &&
+ y2 <= ss->line[ss->slots[j].line])
+ break;
+ }
+
+ /* otherwise append or start a new line */
+ if (j == ss->nSlots)
+ {
+ if (lineLength < itemsPerLine)
+ {
+ lineLength++;
+
+ ss->slots[ss->nSlots].x1 = cx;
+ ss->slots[ss->nSlots].y1 = cy;
+ ss->slots[ss->nSlots].x2 = cx + WIN_W (w);
+ ss->slots[ss->nSlots].y2 = cy + WIN_H (w);
+ ss->slots[ss->nSlots].line = ss->nLine - 1;
+
+ ss->line[ss->nLine - 1] = MAX (ss->line[ss->nLine - 1],
+ ss->slots[ss->nSlots].y2);
+ }
+ else
+ {
+ lineLength = 1;
+
+ cx = ss->spacing;
+ cy = ss->line[ss->nLine - 1] + ss->spacing;
+
+ ss->slots[ss->nSlots].x1 = cx;
+ ss->slots[ss->nSlots].y1 = cy;
+ ss->slots[ss->nSlots].x2 = cx + WIN_W (w);
+ ss->slots[ss->nSlots].y2 = cy + WIN_H (w);
+ ss->slots[ss->nSlots].line = ss->nLine - 1;
+
+ ss->line[ss->nLine] = ss->slots[ss->nSlots].y2;
+
+ ss->nLine++;
+ }
+
+ if (ss->slots[ss->nSlots].y2 > totalHeight)
+ totalHeight = ss->slots[ss->nSlots].y2;
+ }
+ else
+ {
+ ss->slots[ss->nSlots].x1 = ss->slots[j].x1;
+ ss->slots[ss->nSlots].y1 = ss->slots[j].y2 + ss->spacing;
+ ss->slots[ss->nSlots].x2 = ss->slots[ss->nSlots].x1 + WIN_W (w);
+ ss->slots[ss->nSlots].y2 = ss->slots[ss->nSlots].y1 + WIN_H (w);
+ ss->slots[ss->nSlots].line = ss->slots[j].line;
+
+ ss->slots[j].line = 0;
+ }
+
+ cx = ss->slots[ss->nSlots].x2;
+ if (cx > totalWidth)
+ totalWidth = cx;
+
+ cx += ss->spacing;
+
+ sw->slot = &ss->slots[ss->nSlots];
+ sw->adjust = TRUE;
+
+ ss->nSlots++;
+ }
+
+ totalWidth += ss->spacing;
+ totalHeight += ss->spacing;
+
+ scaleW = (float) s->workArea.width / totalWidth;
+ scaleH = (float) s->workArea.height / totalHeight;
+
+ ss->scale = MIN (MIN (scaleH, scaleW), 1.0f);
+
+ for (i = 0; i < ss->nWindows; i++)
+ {
+ SCALE_WINDOW (ss->windows[i]);
+
+ if (sw->slot)
+ {
+ ss->slots[i].y1 += ss->windows[i]->input.top;
+ ss->slots[i].x1 += ss->windows[i]->input.left;
+ ss->slots[i].y1 = (float) ss->slots[i].y1 * ss->scale;
+ ss->slots[i].x1 = (float) ss->slots[i].x1 * ss->scale;
+ ss->slots[i].x1 += s->workArea.x;
+ ss->slots[i].y1 += s->workArea.y;
+ }
+ }
+
+ return TRUE;
+}
+
+static int
+adjustScaleVelocity (CompWindow *w)
+{
+ float dx, dy, ds, adjust, amount;
+ float x1, y1, scale;
+
+ SCALE_SCREEN (w->screen);
+ SCALE_WINDOW (w);
+
+ if (sw->slot)
+ {
+ x1 = sw->slot->x1;
+ y1 = sw->slot->y1;
+ scale = ss->scale;
+ }
+ else
+ {
+ x1 = w->serverX;
+ y1 = w->serverY;
+ scale = 1.0f;
+ }
+
+ dx = x1 - (w->serverX + 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->serverY + 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->serverX;
+ sw->ty = y1 - w->serverY;
+ sw->scale = scale;
+
+ return 0;
+ }
+
+ return 1;
+}
+
+static Bool
+scalePaintScreen (CompScreen *s,
+ const ScreenPaintAttrib *sAttrib,
+ Region region,
+ unsigned int mask)
+{
+ Bool status;
+
+ SCALE_SCREEN (s);
+
+ if (ss->grabIndex)
+ mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS_MASK;
+
+ UNWRAP (ss, s, paintScreen);
+ status = (*s->paintScreen) (s, sAttrib, region, mask);
+ WRAP (ss, s, paintScreen, scalePaintScreen);
+
+ return status;
+}
+
+static void
+scalePreparePaintScreen (CompScreen *s,
+ int msSinceLastPaint)
+{
+ SCALE_SCREEN (s);
+
+ if (ss->grabIndex && ss->state != SCALE_STATE_WAIT)
+ {
+ CompWindow *w;
+ int steps, dx, dy;
+ float amount, chunk;
+
+ amount = msSinceLastPaint * 0.05f * ss->speed;
+ steps = amount / (0.5f * ss->timestep);
+ 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;
+
+ dx = (w->serverX + sw->tx) - w->attrib.x;
+ dy = (w->serverY + sw->ty) - w->attrib.y;
+
+ moveWindow (w, dx, dy, FALSE);
+
+ (*s->setWindowScale) (w, sw->scale, sw->scale);
+ }
+ }
+
+ 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->grabIndex)
+ {
+ if (ss->moreAdjust)
+ {
+ damageScreen (s);
+ }
+ else
+ {
+ if (ss->state == SCALE_STATE_IN)
+ {
+ removeScreenGrab (s, ss->grabIndex, 0);
+ ss->grabIndex = 0;
+ }
+ 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;
+ y1 = w->attrib.y;
+ x2 = x1 + ((float) w->width * sw->scale);
+ y2 = y1 + ((float) w->height * sw->scale);
+
+ if (x1 <= x && y1 <= y && x2 > x && y2 > y)
+ return w;
+ }
+ }
+
+ return 0;
+}
+
+static void
+scaleInitiate (CompScreen *s)
+{
+ SCALE_SCREEN (s);
+ SCALE_DISPLAY (s->display);
+
+ if (ss->state != SCALE_STATE_WAIT &&
+ ss->state != SCALE_STATE_OUT)
+ {
+ if (!ss->grabIndex)
+ ss->grabIndex = pushScreenGrab (s, ss->cursor);
+
+ if (ss->grabIndex)
+ {
+ if (!sd->lastActiveNum)
+ sd->lastActiveNum = s->activeNum - 1;
+
+ if (layoutThumbs (s))
+ {
+ ss->state = SCALE_STATE_OUT;
+ damageScreen (s);
+ }
+ }
+ }
+}
+
+static void
+scaleTerminate (CompScreen *s)
+{
+ SCALE_DISPLAY (s->display);
+ SCALE_SCREEN (s);
+
+ if (ss->grabIndex)
+ {
+ if (ss->state == SCALE_STATE_NONE)
+ {
+ removeScreenGrab (s, ss->grabIndex, 0);
+ ss->grabIndex = 0;
+ }
+ else
+ {
+ CompWindow *w;
+
+ for (w = s->windows; w; w = w->next)
+ {
+ SCALE_WINDOW (w);
+
+ if (sw->slot)
+ {
+ sw->slot = 0;
+ sw->adjust = TRUE;
+ }
+ }
+
+ ss->state = SCALE_STATE_IN;
+
+ damageScreen (s);
+ }
+
+ sd->lastActiveNum = None;
+ }
+}
+
+static void
+scaleSelectWindow (CompWindow *w)
+{
+ activateWindow (w);
+}
+
+static Bool
+scaleSelectWindowAt (CompScreen *s,
+ int x,
+ int y)
+
+{
+ CompWindow *w;
+
+ w = scaleCheckForWindowAt (s, x, y);
+ if (w && isScaleWin (w))
+ {
+ scaleSelectWindow (w);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+scaleNextWindow (CompScreen *s)
+
+{
+ CompWindow *next = NULL;
+ CompWindow *prev = NULL;
+ CompWindow *w;
+
+ SCALE_DISPLAY (s->display);
+
+ for (w = s->windows; w; w = w->next)
+ {
+ if (s->display->activeWindow == w->id)
+ continue;
+
+ if (isScaleWin (w))
+ {
+ if (w->activeNum < sd->lastActiveNum)
+ {
+ if (next)
+ {
+ if (w->activeNum > next->activeNum)
+ next = w;
+ }
+ else
+ next = w;
+ }
+ else if (w->activeNum > sd->lastActiveNum)
+ {
+ if (prev)
+ {
+ if (w->activeNum < prev->activeNum)
+ prev = w;
+ }
+ else
+ prev = w;
+ }
+ }
+ }
+
+ if (next)
+ w = next;
+ else
+ w = prev;
+
+ if (w)
+ {
+ sd->lastActiveNum = w->activeNum;
+
+ scaleSelectWindow (w);
+ }
+}
+
+static void
+scaleWindowRemove (CompDisplay *d,
+ Window id)
+{
+ CompWindow *w;
+
+ w = findWindowAtDisplay (d, id);
+ if (w)
+ {
+ SCALE_SCREEN (w->screen);
+
+ if (ss->grabIndex)
+ {
+ 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;
+ }
+ }
+ }
+ }
+ }
+}
+
+static void
+scaleHandleEvent (CompDisplay *d,
+ XEvent *event)
+{
+ CompScreen *s;
+
+ SCALE_DISPLAY (d);
+
+ switch (event->type) {
+ case KeyPress:
+ case KeyRelease:
+ s = findScreenAtDisplay (d, event->xkey.root);
+ if (s)
+ {
+ int state;
+
+ SCALE_SCREEN (s);
+
+ state = ss->state;
+
+ if (EV_KEY (&ss->opt[SCALE_SCREEN_OPTION_INITIATE], event))
+ scaleInitiate (s);
+
+ if (ss->grabIndex &&
+ EV_KEY (&ss->opt[SCALE_SCREEN_OPTION_NEXT_WINDOW], event))
+ scaleNextWindow (s);
+
+ if (state == ss->state &&
+ (EV_KEY (&ss->opt[SCALE_SCREEN_OPTION_TERMINATE], event) ||
+ (event->type == KeyPress &&
+ event->xkey.keycode == s->escapeKeyCode)))
+ scaleTerminate (s);
+ }
+ break;
+ case ButtonPress:
+ case ButtonRelease:
+ s = findScreenAtDisplay (d, event->xbutton.root);
+ if (s)
+ {
+ int state;
+
+ SCALE_SCREEN (s);
+
+ state = ss->state;
+
+ if (ss->grabIndex &&
+ ss->state != SCALE_STATE_IN &&
+ event->type == ButtonPress)
+ {
+ if (scaleSelectWindowAt (s,
+ event->xbutton.x_root,
+ event->xbutton.y_root))
+ scaleTerminate (s);
+ }
+
+ if (EV_BUTTON (&ss->opt[SCALE_SCREEN_OPTION_INITIATE], event))
+ scaleInitiate (s);
+
+ if (ss->grabIndex &&
+ EV_BUTTON (&ss->opt[SCALE_SCREEN_OPTION_NEXT_WINDOW], event))
+ scaleNextWindow (s);
+
+ if (state == ss->state &&
+ EV_BUTTON (&ss->opt[SCALE_SCREEN_OPTION_TERMINATE], event))
+ scaleTerminate (s);
+ }
+ break;
+ case MotionNotify:
+ s = findScreenAtDisplay (d, event->xmotion.root);
+ if (s)
+ {
+ SCALE_SCREEN (s);
+
+ if (ss->grabIndex &&
+ ss->state != SCALE_STATE_IN &&
+ ss->opt[SCALE_SCREEN_OPTION_SLOPPY_FOCUS].value.b)
+ scaleSelectWindowAt (s,
+ event->xmotion.x_root,
+ event->xmotion.y_root);
+ }
+ default:
+ break;
+ }
+
+ 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;
+
+ SCALE_SCREEN (w->screen);
+
+ if (initial)
+ {
+ if (isScaleWin (w))
+ {
+ if (ss->grabIndex && layoutThumbs (w->screen))
+ {
+ ss->state = SCALE_STATE_OUT;
+ damageScreen (w->screen);
+ }
+ }
+ }
+
+ UNWRAP (ss, w->screen, damageWindowRect);
+ status = (*w->screen->damageWindowRect) (w, initial, rect);
+ WRAP (ss, w->screen, damageWindowRect, scaleDamageWindowRect);
+
+ return status;
+}
+
+static Bool
+scaleInitDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ ScaleDisplay *sd;
+
+ sd = malloc (sizeof (ScaleDisplay));
+ if (!sd)
+ return FALSE;
+
+ sd->screenPrivateIndex = allocateScreenPrivateIndex (d);
+ if (sd->screenPrivateIndex < 0)
+ {
+ free (sd);
+ return FALSE;
+ }
+
+ sd->lastActiveNum = None;
+
+ WRAP (sd, d, handleEvent, scaleHandleEvent);
+
+ d->privates[displayPrivateIndex].ptr = sd;
+
+ return TRUE;
+}
+
+static void
+scaleFiniDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ SCALE_DISPLAY (d);
+
+ freeScreenPrivateIndex (d, sd->screenPrivateIndex);
+
+ UNWRAP (sd, d, handleEvent);
+
+ free (sd);
+}
+
+static Bool
+scaleInitScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ ScaleScreen *ss;
+
+ SCALE_DISPLAY (s->display);
+
+ ss = malloc (sizeof (ScaleScreen));
+ if (!ss)
+ return FALSE;
+
+ ss->windowPrivateIndex = allocateWindowPrivateIndex (s);
+ if (ss->windowPrivateIndex < 0)
+ {
+ free (ss);
+ return FALSE;
+ }
+
+ ss->grabIndex = 0;
+
+ ss->state = SCALE_STATE_NONE;
+
+ ss->slots = 0;
+ ss->slotsSize = 0;
+
+ ss->windows = 0;
+ ss->windowsSize = 0;
+
+ ss->line = 0;
+ ss->lineSize = 0;
+
+ ss->scale = 1.0f;
+
+ ss->spacing = SCALE_SPACING_DEFAULT;
+
+ ss->speed = SCALE_SPEED_DEFAULT;
+ ss->timestep = SCALE_TIMESTEP_DEFAULT;
+
+ scaleScreenInitOptions (ss, s->display->display);
+
+ addScreenBinding (s, &ss->opt[SCALE_SCREEN_OPTION_INITIATE].value.bind);
+
+ WRAP (ss, s, preparePaintScreen, scalePreparePaintScreen);
+ WRAP (ss, s, donePaintScreen, scaleDonePaintScreen);
+ WRAP (ss, s, paintScreen, scalePaintScreen);
+ WRAP (ss, s, paintWindow, scalePaintWindow);
+ WRAP (ss, s, damageWindowRect, scaleDamageWindowRect);
+
+ ss->cursor = XCreateFontCursor (s->display->display, XC_left_ptr);
+
+ s->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, paintScreen);
+ UNWRAP (ss, s, paintWindow);
+ UNWRAP (ss, s, damageWindowRect);
+
+ if (ss->slotsSize)
+ free (ss->slots);
+
+ if (ss->lineSize)
+ free (ss->line);
+
+ if (ss->windowsSize)
+ free (ss->windows);
+
+ 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;
+
+ w->privates[ss->windowPrivateIndex].ptr = sw;
+
+ return TRUE;
+}
+
+static void
+scaleFiniWindow (CompPlugin *p,
+ CompWindow *w)
+{
+ SCALE_WINDOW (w);
+
+ free (sw);
+}
+
+static Bool
+scaleInit (CompPlugin *p)
+{
+ displayPrivateIndex = allocateDisplayPrivateIndex ();
+ if (displayPrivateIndex < 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+scaleFini (CompPlugin *p)
+{
+ if (displayPrivateIndex >= 0)
+ freeDisplayPrivateIndex (displayPrivateIndex);
+}
+
+CompPluginVTable scaleVTable = {
+ "scale",
+ "Scale",
+ "Scale windows",
+ scaleInit,
+ scaleFini,
+ scaleInitDisplay,
+ scaleFiniDisplay,
+ scaleInitScreen,
+ scaleFiniScreen,
+ scaleInitWindow,
+ scaleFiniWindow,
+ 0, /* GetDisplayOptions */
+ 0, /* SetDisplayOption */
+ scaleGetScreenOptions,
+ scaleSetScreenOption,
+ 0, /* Deps */
+ 0 /* nDeps */
+};
+
+CompPluginVTable *
+getCompPluginInfo (void)
+{
+ return &scaleVTable;
+}
diff --git a/plugins/switcher.c b/plugins/switcher.c
new file mode 100644
index 00000000..09b774ae
--- /dev/null
+++ b/plugins/switcher.c
@@ -0,0 +1,1565 @@
+/*
+ * 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>
+
+#ifndef GTK_DISABLE_DEPRECATED
+#define GTK_DISABLE_DEPRECATED
+#endif
+
+#include <gtk/gtk.h>
+#include <gdk/gdkx.h>
+
+#include <X11/Xatom.h>
+
+#include <compiz.h>
+
+#define SWITCH_INITIATE_KEY_DEFAULT "Tab"
+#define SWITCH_INITIATE_MODIFIERS_DEFAULT (CompPressMask | CompAltMask)
+
+#define SWITCH_TERMINATE_KEY_DEFAULT "Alt_L"
+#define SWITCH_TERMINATE_MODIFIERS_DEFAULT CompReleaseMask
+
+#define SWITCH_NEXT_WINDOW_KEY_DEFAULT "Tab"
+#define SWITCH_NEXT_WINDOW_MODIFIERS_DEFAULT (CompPressMask | CompAltMask)
+
+#define SWITCH_SPEED_DEFAULT 1.5f
+#define SWITCH_SPEED_MIN 0.1f
+#define SWITCH_SPEED_MAX 50.0f
+#define SWITCH_SPEED_PRECISION 0.1f
+
+#define SWITCH_TIMESTEP_DEFAULT 1.2f
+#define SWITCH_TIMESTEP_MIN 0.1f
+#define SWITCH_TIMESTEP_MAX 50.0f
+#define SWITCH_TIMESTEP_PRECISION 0.1f
+
+static char *winType[] = {
+ "Toolbar",
+ "Utility",
+ "Dialog",
+ "Fullscreen",
+ "Normal",
+};
+#define N_WIN_TYPE (sizeof (winType) / sizeof (winType[0]))
+
+static int displayPrivateIndex;
+
+typedef struct _SwitchDisplay {
+ int screenPrivateIndex;
+ HandleEventProc handleEvent;
+
+ Atom popupWinAtom;
+ Atom selectWinAtom;
+} SwitchDisplay;
+
+#define SWITCH_SCREEN_OPTION_INITIATE 0
+#define SWITCH_SCREEN_OPTION_TERMINATE 1
+#define SWITCH_SCREEN_OPTION_NEXT_WINDOW 2
+#define SWITCH_SCREEN_OPTION_SPEED 3
+#define SWITCH_SCREEN_OPTION_TIMESTEP 4
+#define SWITCH_SCREEN_OPTION_WINDOW_TYPE 5
+#define SWITCH_SCREEN_OPTION_NUM 6
+
+typedef struct _SwitchScreen {
+ PreparePaintScreenProc preparePaintScreen;
+ DonePaintScreenProc donePaintScreen;
+ PaintWindowProc paintWindow;
+ DamageWindowRectProc damageWindowRect;
+
+ CompOption opt[SWITCH_SCREEN_OPTION_NUM];
+
+ pid_t pid;
+ Window popupWindow;
+
+ Window selectedWindow;
+ unsigned int lastActiveNum;
+
+ float speed;
+ float timestep;
+
+ unsigned int wMask;
+
+ int grabIndex;
+
+ int moreAdjust;
+ GLfloat velocity;
+
+ CompWindow **windows;
+ int windowsSize;
+ int nWindows;
+
+ int pos;
+ int move;
+} SwitchScreen;
+
+#define POPUP_WIN_PROP "_SWITCH_POPUP_WINDOW"
+#define SELECT_WIN_PROP "_SWITCH_SELECT_WINDOW"
+
+static Atom visibleNameAtom;
+static Atom nameAtom;
+static Atom utf8StringAtom;
+
+static GtkWidget *topPane, *bottomPane;
+static GdkWindow *foreign = NULL;
+
+#define WIDTH 212
+#define HEIGHT 192
+#define BORDER 6
+#define SPACE 10
+
+#define BOX_WIDTH 3
+
+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) + (BORDER << 1) + (SPACE << 1))
+#define WINDOW_HEIGHT (HEIGHT + (BORDER << 1) + (SPACE << 1) + 40)
+
+#define GET_SWITCH_DISPLAY(d) \
+ ((SwitchDisplay *) (d)->privates[displayPrivateIndex].ptr)
+
+#define SWITCH_DISPLAY(d) \
+ SwitchDisplay *sd = GET_SWITCH_DISPLAY (d)
+
+#define GET_SWITCH_SCREEN(s, sd) \
+ ((SwitchScreen *) (s)->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 (CompScreen *screen,
+ int *count)
+{
+ SWITCH_SCREEN (screen);
+
+ *count = NUM_OPTIONS (ss);
+ return ss->opt;
+}
+
+static Bool
+switchSetScreenOption (CompScreen *screen,
+ 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_INITIATE:
+ if (addScreenBinding (screen, &value->bind))
+ {
+ removeScreenBinding (screen, &o->value.bind);
+
+ if (compSetBindingOption (o, value))
+ return TRUE;
+ }
+ break;
+ case SWITCH_SCREEN_OPTION_TERMINATE:
+ case SWITCH_SCREEN_OPTION_NEXT_WINDOW:
+ if (compSetBindingOption (o, value))
+ return TRUE;
+ break;
+ case SWITCH_SCREEN_OPTION_SPEED:
+ if (compSetFloatOption (o, value))
+ {
+ ss->speed = o->value.f;
+ return TRUE;
+ }
+ break;
+ case SWITCH_SCREEN_OPTION_TIMESTEP:
+ if (compSetFloatOption (o, value))
+ {
+ ss->timestep = o->value.f;
+ return TRUE;
+ }
+ break;
+ case SWITCH_SCREEN_OPTION_WINDOW_TYPE:
+ if (compSetOptionList (o, value))
+ {
+ ss->wMask = compWindowTypeMaskFromStringList (&o->value);
+ return TRUE;
+ }
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+static void
+switchScreenInitOptions (SwitchScreen *ss,
+ Display *display)
+{
+ CompOption *o;
+ int i;
+
+ o = &ss->opt[SWITCH_SCREEN_OPTION_INITIATE];
+ o->name = "initiate";
+ o->shortDesc = "Initiate";
+ o->longDesc = "Layout and start transforming windows";
+ o->type = CompOptionTypeBinding;
+ o->value.bind.type = CompBindingTypeKey;
+ o->value.bind.u.key.modifiers = SWITCH_INITIATE_MODIFIERS_DEFAULT;
+ o->value.bind.u.key.keycode =
+ XKeysymToKeycode (display,
+ XStringToKeysym (SWITCH_INITIATE_KEY_DEFAULT));
+
+ o = &ss->opt[SWITCH_SCREEN_OPTION_TERMINATE];
+ o->name = "terminate";
+ o->shortDesc = "Terminate";
+ o->longDesc = "Return from expose view";
+ o->type = CompOptionTypeBinding;
+ o->value.bind.type = CompBindingTypeKey;
+ o->value.bind.u.key.modifiers = SWITCH_TERMINATE_MODIFIERS_DEFAULT;
+ o->value.bind.u.key.keycode =
+ XKeysymToKeycode (display,
+ XStringToKeysym (SWITCH_TERMINATE_KEY_DEFAULT));
+
+ o = &ss->opt[SWITCH_SCREEN_OPTION_NEXT_WINDOW];
+ o->name = "next_window";
+ o->shortDesc = "Next Window";
+ o->longDesc = "Focus next window";
+ o->type = CompOptionTypeBinding;
+ o->value.bind.type = CompBindingTypeKey;
+ o->value.bind.u.key.modifiers = SWITCH_NEXT_WINDOW_MODIFIERS_DEFAULT;
+ o->value.bind.u.key.keycode =
+ XKeysymToKeycode (display,
+ XStringToKeysym (SWITCH_NEXT_WINDOW_KEY_DEFAULT));
+
+ o = &ss->opt[SWITCH_SCREEN_OPTION_SPEED];
+ o->name = "speed";
+ o->shortDesc = "Speed";
+ o->longDesc = "Expose speed";
+ o->type = CompOptionTypeFloat;
+ o->value.f = SWITCH_SPEED_DEFAULT;
+ o->rest.f.min = SWITCH_SPEED_MIN;
+ o->rest.f.max = SWITCH_SPEED_MAX;
+ o->rest.f.precision = SWITCH_SPEED_PRECISION;
+
+ o = &ss->opt[SWITCH_SCREEN_OPTION_TIMESTEP];
+ o->name = "timestep";
+ o->shortDesc = "Timestep";
+ o->longDesc = "Expose timestep";
+ o->type = CompOptionTypeFloat;
+ o->value.f = SWITCH_TIMESTEP_DEFAULT;
+ o->rest.f.min = SWITCH_TIMESTEP_MIN;
+ o->rest.f.max = SWITCH_TIMESTEP_MAX;
+ o->rest.f.precision = SWITCH_TIMESTEP_PRECISION;
+
+ o = &ss->opt[SWITCH_SCREEN_OPTION_WINDOW_TYPE];
+ o->name = "window_types";
+ o->shortDesc = "Window Types";
+ o->longDesc = "Window types that should scaled in expose mode";
+ o->type = CompOptionTypeList;
+ o->value.list.type = CompOptionTypeString;
+ o->value.list.nValue = N_WIN_TYPE;
+ o->value.list.value = malloc (sizeof (CompOptionValue) * N_WIN_TYPE);
+ for (i = 0; i < N_WIN_TYPE; i++)
+ o->value.list.value[i].s = strdup (winType[i]);
+ o->rest.s.string = windowTypeString;
+ o->rest.s.nString = nWindowTypeString;
+
+ ss->wMask = compWindowTypeMaskFromStringList (&o->value);
+}
+
+static char *
+text_property_to_utf8 (const XTextProperty *prop)
+{
+ char **list;
+ int count;
+ char *retval;
+
+ list = NULL;
+
+ count =
+ gdk_text_property_to_utf8_list (gdk_x11_xatom_to_atom (prop->encoding),
+ prop->format,
+ prop->value,
+ prop->nitems,
+ &list);
+ if (count == 0)
+ return NULL;
+
+ retval = list[0];
+ list[0] = g_strdup ("");
+
+ g_strfreev (list);
+
+ return retval;
+}
+
+static char *
+_get_text_property (Window xwindow,
+ Atom atom)
+{
+ XTextProperty text;
+ char *retval;
+
+ text.nitems = 0;
+ if (XGetTextProperty (gdk_display,
+ xwindow,
+ &text,
+ atom))
+ {
+ retval = text_property_to_utf8 (&text);
+
+ if (text.value)
+ XFree (text.value);
+ }
+ else
+ {
+ retval = NULL;
+ }
+
+ return retval;
+}
+
+static char *
+_get_utf8_property (Window xwindow,
+ Atom atom)
+{
+ Atom type;
+ int format;
+ gulong nitems;
+ gulong bytes_after;
+ gchar *val;
+ int result;
+ gchar *retval;
+
+ type = None;
+ val = NULL;
+
+ result = XGetWindowProperty (gdk_display, xwindow, atom,
+ 0, G_MAXLONG,
+ FALSE, utf8StringAtom,
+ &type, &format, &nitems,
+ &bytes_after, (guchar **) &val);
+
+ if (result != Success)
+ return NULL;
+
+ if (type != utf8StringAtom || format != 8 || nitems == 0)
+ {
+ if (val)
+ XFree (val);
+
+ return NULL;
+ }
+
+ if (!g_utf8_validate (val, nitems, NULL))
+ {
+ XFree (val);
+
+ return NULL;
+ }
+
+ retval = g_strndup (val, nitems);
+
+ XFree (val);
+
+ return retval;
+}
+
+static char *
+_get_window_name (Window xwindow)
+{
+ char *name;
+
+ name = _get_utf8_property (xwindow, visibleNameAtom);
+ if (!name)
+ {
+ name = _get_utf8_property (xwindow, nameAtom);
+ if (!name)
+ name = _get_text_property (xwindow, XA_WM_NAME);
+ }
+
+ return name;
+}
+
+static gboolean
+paintWidget (GtkWidget *widget,
+ GdkEventExpose *event,
+ gpointer data)
+{
+ cairo_t *cr;
+ GList *child;
+
+ cr = gdk_cairo_create (widget->window);
+
+ /* draw colored border */
+ gdk_draw_rectangle (widget->window,
+ widget->style->bg_gc[GTK_STATE_SELECTED],
+ TRUE,
+ widget->allocation.x,
+ widget->allocation.y,
+ widget->allocation.width - 1,
+ widget->allocation.height - 1);
+
+ /* draw black outer outline */
+ gdk_draw_rectangle (widget->window,
+ widget->style->black_gc,
+ FALSE,
+ widget->allocation.x,
+ widget->allocation.y,
+ widget->allocation.width - 1,
+ widget->allocation.height - 1);
+
+ /* draw inner outline */
+ gdk_draw_rectangle (widget->window,
+ widget->style->fg_gc[GTK_STATE_INSENSITIVE],
+ FALSE,
+ widget->allocation.x + BORDER - 1,
+ widget->allocation.y + BORDER - 1,
+ widget->allocation.width - 2 * BORDER + 1,
+ widget->allocation.height - 2 * BORDER + 1);
+
+ /* draw top pane background */
+ cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+
+ cairo_set_source_rgba (cr,
+ widget->style->bg[GTK_STATE_NORMAL].red / 65535.0,
+ widget->style->bg[GTK_STATE_NORMAL].green / 65535.0,
+ widget->style->bg[GTK_STATE_NORMAL].blue / 65535.0,
+ 0.8);
+
+ cairo_rectangle (cr,
+ topPane->allocation.x,
+ topPane->allocation.y,
+ topPane->allocation.width,
+ topPane->allocation.height);
+
+ cairo_fill (cr);
+
+ cairo_rectangle (cr,
+ topPane->allocation.x,
+ topPane->allocation.y,
+ topPane->allocation.width,
+ topPane->allocation.height);
+
+ cairo_fill (cr);
+
+ /* draw bottom pane background */
+ cairo_set_source_rgba (cr,
+ widget->style->bg[GTK_STATE_ACTIVE].red / 65535.0,
+ widget->style->bg[GTK_STATE_ACTIVE].green / 65535.0,
+ widget->style->bg[GTK_STATE_ACTIVE].blue / 65535.0,
+ 0.8);
+
+ cairo_rectangle (cr,
+ bottomPane->allocation.x,
+ bottomPane->allocation.y,
+ bottomPane->allocation.width,
+ bottomPane->allocation.height);
+
+ cairo_fill (cr);
+
+ /* draw pane separator */
+ gdk_draw_line (widget->window,
+ widget->style->dark_gc[GTK_STATE_NORMAL],
+ bottomPane->allocation.x,
+ bottomPane->allocation.y,
+ bottomPane->allocation.x + bottomPane->allocation.width - 1,
+ bottomPane->allocation.y);
+
+ cairo_destroy (cr);
+
+ child = gtk_container_get_children (GTK_CONTAINER (widget));
+
+ for (; child; child = child->next)
+ gtk_container_propagate_expose (GTK_CONTAINER (widget),
+ GTK_WIDGET (child->data),
+ event);
+
+ return TRUE;
+}
+
+static GdkFilterReturn
+switchNameChangeFilterFunc (GdkXEvent *gdkxevent,
+ GdkEvent *event,
+ gpointer data)
+{
+ Display *xdisplay;
+ XEvent *xevent = gdkxevent;
+ GtkWidget *label = data;
+ gchar *name;
+ gchar *markup = NULL;
+
+ xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
+
+ switch (xevent->type) {
+ case PropertyNotify:
+ if (!foreign || GDK_WINDOW_XID (foreign) != xevent->xproperty.window)
+ return GDK_FILTER_CONTINUE;
+
+ gdk_error_trap_push ();
+
+ name = _get_window_name (xevent->xproperty.window);
+
+ gdk_flush ();
+ if (!gdk_error_trap_pop () && name)
+ {
+ markup = g_markup_printf_escaped ("<span size=\"x-large\">"
+ "%s"
+ "</span>",
+ name);
+ g_free (name);
+ }
+
+ if (markup)
+ {
+ gtk_label_set_markup (GTK_LABEL (label), markup);
+ g_free (markup);
+ }
+ else
+ gtk_label_set_text (GTK_LABEL (label), "");
+ default:
+ break;
+ }
+
+ return GDK_FILTER_CONTINUE;
+}
+
+static GdkFilterReturn
+switchSelectWindowFilterFunc (GdkXEvent *gdkxevent,
+ GdkEvent *event,
+ gpointer data)
+{
+ Display *xdisplay;
+ XEvent *xevent = gdkxevent;
+ GtkWidget *label = data;
+ gchar *markup = NULL;
+
+ xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
+
+ switch (xevent->type) {
+ case ClientMessage:
+ if (foreign)
+ {
+ gdk_error_trap_push ();
+
+ gdk_window_set_events (foreign, 0);
+ gdk_window_unref (foreign);
+
+ gdk_flush ();
+ gdk_error_trap_pop ();
+ }
+
+ gdk_error_trap_push ();
+
+ foreign = gdk_window_foreign_new (xevent->xclient.data.l[0]);
+
+ gdk_flush ();
+ if (!gdk_error_trap_pop () && foreign)
+ {
+ gchar *name;
+
+ gdk_window_add_filter (foreign,
+ switchNameChangeFilterFunc,
+ data);
+
+ gdk_error_trap_push ();
+
+ gdk_window_set_events (foreign, GDK_PROPERTY_CHANGE_MASK);
+ name = _get_window_name (xevent->xclient.data.l[0]);
+
+ gdk_flush ();
+ if (!gdk_error_trap_pop () && name)
+ {
+ markup = g_markup_printf_escaped ("<span size=\"x-large\">"
+ "%s"
+ "</span>",
+ name);
+ g_free (name);
+ }
+ }
+
+ if (markup)
+ {
+ gtk_label_set_markup (GTK_LABEL (label), markup);
+ g_free (markup);
+ }
+ else
+ gtk_label_set_text (GTK_LABEL (label), "");
+ default:
+ break;
+ }
+
+ return GDK_FILTER_CONTINUE;
+}
+
+
+static void
+switchSendPopupNotify (GdkScreen *screen,
+ Window xwindow)
+{
+ Display *xdisplay;
+ Screen *xscreen;
+ XEvent xev;
+
+ xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
+ xscreen = gdk_x11_screen_get_xscreen (screen);
+
+ xev.xclient.type = ClientMessage;
+ xev.xclient.serial = 0;
+ xev.xclient.send_event = TRUE;
+ xev.xclient.display = xdisplay;
+ xev.xclient.window = RootWindowOfScreen (xscreen);
+ xev.xclient.message_type = XInternAtom (xdisplay, POPUP_WIN_PROP, FALSE);
+ xev.xclient.format = 32;
+ xev.xclient.data.l[0] = xwindow;
+ xev.xclient.data.l[1] = 0;
+ xev.xclient.data.l[2] = 0;
+ xev.xclient.data.l[3] = 0;
+ xev.xclient.data.l[4] = 0;
+
+ XSendEvent (xdisplay,
+ RootWindowOfScreen (xscreen),
+ FALSE,
+ SubstructureRedirectMask | SubstructureNotifyMask,
+ &xev);
+}
+
+static void
+sendSelectWindowMessage (CompScreen *s)
+{
+ XEvent xev;
+
+ SWITCH_DISPLAY (s->display);
+ SWITCH_SCREEN (s);
+
+ xev.xclient.type = ClientMessage;
+ xev.xclient.serial = 0;
+ xev.xclient.send_event = TRUE;
+ xev.xclient.display = s->display->display;
+ xev.xclient.window = ss->popupWindow;
+ xev.xclient.message_type = sd->selectWinAtom;
+ xev.xclient.format = 32;
+ xev.xclient.data.l[0] = ss->selectedWindow;
+ xev.xclient.data.l[1] = 0;
+ xev.xclient.data.l[2] = 0;
+ xev.xclient.data.l[3] = 0;
+ xev.xclient.data.l[4] = 0;
+
+ XSendEvent (s->display->display,
+ ss->popupWindow,
+ FALSE,
+ SubstructureRedirectMask | SubstructureNotifyMask,
+ &xev);
+}
+
+static Bool
+isSwitchWin (CompWindow *w)
+{
+ SWITCH_SCREEN (w->screen);
+
+ if (!w->mapNum || w->attrib.map_state != IsViewable)
+ return FALSE;
+
+ if (w->attrib.override_redirect)
+ return FALSE;
+
+ if (!(ss->wMask & w->type))
+ return FALSE;
+
+ if (w->state & CompWindowStateSkipPagerMask)
+ return FALSE;
+
+ return TRUE;
+}
+
+static int
+compareWindows (const void *elem1,
+ const void *elem2)
+{
+ CompWindow *w1 = *((CompWindow **) elem1);
+ CompWindow *w2 = *((CompWindow **) elem2);
+
+ 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)
+{
+ 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]);
+ }
+
+ count -= (count + 1) & 1;
+ if (count < 3)
+ count = 3;
+
+ ss->pos = ((count >> 1) - ss->nWindows) * WIDTH;
+ ss->move = 0;
+
+ if (ss->popupWindow)
+ XResizeWindow (s->display->display, ss->popupWindow,
+ WINDOW_WIDTH (count),
+ WINDOW_HEIGHT);
+}
+
+static void
+switchActivateWindow (CompWindow *w)
+{
+ if ((*w->screen->focusWindow) (w))
+ {
+ activateWindow (w);
+ }
+ else
+ sendWindowActivationRequest (w->screen, w->id);
+}
+
+static void
+switchNextWindow (CompScreen *s)
+{
+ CompWindow *next = NULL;
+ CompWindow *prev = NULL;
+ CompWindow *w;
+
+ SWITCH_SCREEN (s);
+
+ if (!ss->grabIndex)
+ return;
+
+ for (w = s->windows; w; w = w->next)
+ {
+ if (w->id == ss->selectedWindow)
+ continue;
+
+ if (isSwitchWin (w))
+ {
+ if (w->activeNum < ss->lastActiveNum)
+ {
+ if (next)
+ {
+ if (w->activeNum > next->activeNum)
+ next = w;
+ }
+ else
+ next = w;
+ }
+ else if (w->activeNum > ss->lastActiveNum)
+ {
+ if (prev)
+ {
+ if (w->activeNum > prev->activeNum)
+ prev = w;
+ }
+ else
+ prev = w;
+ }
+ }
+ }
+
+ if (next)
+ w = next;
+ else
+ w = prev;
+
+ if (w)
+ {
+ ss->lastActiveNum = w->activeNum;
+ ss->selectedWindow = w->id;
+
+ ss->move -= WIDTH;
+ ss->moreAdjust = 1;
+
+ if (ss->popupWindow)
+ {
+ w = findWindowAtScreen (s, ss->popupWindow);
+ if (w)
+ addWindowDamage (w);
+
+ sendSelectWindowMessage (s);
+ }
+ }
+}
+
+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 void
+switchInitiate (CompScreen *s)
+{
+ int count;
+
+ SWITCH_SCREEN (s);
+
+ if (ss->grabIndex)
+ return;
+
+ count = switchCountWindows (s);
+ if (count < 2)
+ return;
+
+ if (!ss->pid)
+ {
+ ss->pid = fork ();
+
+ if (ss->pid == 0)
+ {
+ GtkWidget *window, *vbox;
+ GdkDisplay *display;
+ GdkScreen *screen;
+ GdkVisual *visual;
+ GdkAtom selectWinAtom;
+
+ gtk_init (&programArgc, &programArgv);
+
+ window = gtk_window_new (GTK_WINDOW_POPUP);
+
+ /* check for visual with depth 32 */
+ visual = gdk_visual_get_best_with_depth (32);
+ if (visual)
+ {
+ GdkColormap *colormap;
+
+ /* create colormap for depth 32 visual and use it with our
+ top level window. */
+ colormap = gdk_colormap_new (visual, FALSE);
+ gtk_widget_set_colormap (window, colormap);
+ }
+
+ display = gdk_display_get_default ();
+
+ screen = gdk_display_get_screen (display, s->screenNum);
+
+ gtk_window_set_screen (GTK_WINDOW (window), screen);
+
+ gtk_window_set_position (GTK_WINDOW (window),
+ GTK_WIN_POS_CENTER_ALWAYS);
+
+ count -= (count + 1) & 1;
+ if (count < 3)
+ count = 3;
+
+ /* enable resizing, to get never-shrink behavior */
+ gtk_window_set_resizable (GTK_WINDOW (window), TRUE);
+ gtk_window_set_default_size (GTK_WINDOW (window),
+ WINDOW_WIDTH (count),
+ WINDOW_HEIGHT);
+
+ vbox = gtk_vbox_new (FALSE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), BORDER);
+ gtk_container_add (GTK_CONTAINER (window), vbox);
+
+ topPane = gtk_event_box_new ();
+ gtk_event_box_set_visible_window (GTK_EVENT_BOX (topPane), FALSE);
+ gtk_widget_set_size_request (topPane, 0, HEIGHT + (SPACE << 1));
+ gtk_box_pack_start (GTK_BOX (vbox), topPane, FALSE, FALSE, 0);
+
+ bottomPane = gtk_label_new ("");
+ gtk_label_set_ellipsize (GTK_LABEL (bottomPane),
+ PANGO_ELLIPSIZE_END);
+ gtk_label_set_line_wrap (GTK_LABEL (bottomPane), FALSE);
+ gtk_box_pack_end (GTK_BOX (vbox), bottomPane, TRUE, TRUE, 0);
+
+ gtk_widget_realize (window);
+
+ selectWinAtom = gdk_atom_intern (SELECT_WIN_PROP, FALSE);
+
+ visibleNameAtom = XInternAtom (GDK_DISPLAY_XDISPLAY (display),
+ "_NET_WM_VISIBLE_NAME", FALSE);
+ nameAtom = XInternAtom (GDK_DISPLAY_XDISPLAY (display),
+ "_NET_WM_NAME", FALSE);
+ utf8StringAtom = XInternAtom (GDK_DISPLAY_XDISPLAY (display),
+ "UTF8_STRING", FALSE);
+
+ gdk_display_add_client_message_filter (display,
+ selectWinAtom,
+ switchSelectWindowFilterFunc,
+ bottomPane);
+ gdk_window_set_events (window->window, GDK_ALL_EVENTS_MASK);
+
+ g_signal_connect (G_OBJECT (window),
+ "expose-event",
+ G_CALLBACK (paintWidget),
+ NULL);
+
+ gdk_window_set_type_hint (window->window,
+ GDK_WINDOW_TYPE_HINT_NORMAL);
+
+ gtk_widget_show_all (window);
+
+ switchSendPopupNotify (screen, GDK_WINDOW_XID (window->window));
+
+ gtk_main ();
+
+ exit (0);
+ }
+ }
+
+ if (!ss->grabIndex)
+ {
+ ss->grabIndex = pushScreenGrab (s, s->invisibleCursor);
+
+ if (ss->grabIndex)
+ {
+ ss->lastActiveNum = s->activeNum;
+ ss->selectedWindow = s->display->activeWindow;
+
+ switchUpdateWindowList (s, count);
+
+ if (ss->popupWindow)
+ XMapRaised (s->display->display, ss->popupWindow);
+ }
+ }
+}
+
+static void
+switchTerminate (CompScreen *s,
+ Bool select)
+{
+ SWITCH_SCREEN (s);
+
+ if (ss->grabIndex)
+ {
+ if (ss->popupWindow)
+ XUnmapWindow (s->display->display, ss->popupWindow);
+
+ removeScreenGrab (s, ss->grabIndex, 0);
+ ss->grabIndex = 0;
+
+ if (select && ss->selectedWindow)
+ {
+ CompWindow *w;
+
+ w = findWindowAtScreen (s, ss->selectedWindow);
+ if (w)
+ switchActivateWindow (w);
+ }
+
+ ss->selectedWindow = None;
+ ss->lastActiveNum = 0;
+ }
+}
+
+static void
+switchWindowRemove (CompDisplay *d,
+ Window id)
+{
+ CompWindow *w;
+
+ w = findWindowAtDisplay (d, id);
+ if (w)
+ {
+ SWITCH_SCREEN (w->screen);
+
+ if (ss->grabIndex)
+ {
+ int i;
+
+ for (i = 0; i < ss->nWindows; i++)
+ {
+ if (ss->windows[i] == w)
+ {
+ ss->lastActiveNum = w->screen->activeNum;
+ ss->selectedWindow = d->activeWindow;
+
+ switchUpdateWindowList (w->screen,
+ switchCountWindows (w->screen));
+
+ break;
+ }
+ }
+ }
+ }
+}
+
+static void
+switchHandleEvent (CompDisplay *d,
+ XEvent *event)
+{
+ CompScreen *s;
+
+ SWITCH_DISPLAY (d);
+
+ switch (event->type) {
+ case KeyPress:
+ case KeyRelease:
+ s = findScreenAtDisplay (d, event->xkey.root);
+ if (s)
+ {
+ SWITCH_SCREEN (s);
+
+ if (EV_KEY (&ss->opt[SWITCH_SCREEN_OPTION_INITIATE], event))
+ switchInitiate (s);
+
+ if (EV_KEY (&ss->opt[SWITCH_SCREEN_OPTION_NEXT_WINDOW], event))
+ switchNextWindow (s);
+
+ if (EV_KEY (&ss->opt[SWITCH_SCREEN_OPTION_TERMINATE], event))
+ switchTerminate (s, TRUE);
+ else if (event->type == KeyPress &&
+ event->xkey.keycode == s->escapeKeyCode)
+ switchTerminate (s, FALSE);
+ }
+ break;
+ case ClientMessage:
+ s = findScreenAtDisplay (d, event->xclient.window);
+ if (s)
+ {
+ SWITCH_SCREEN (s);
+
+ if (event->xclient.message_type == sd->popupWinAtom)
+ {
+ ss->popupWindow = event->xclient.data.l[0];
+
+ if (ss->grabIndex)
+ sendSelectWindowMessage (s);
+ else
+ XUnmapWindow (d->display, ss->popupWindow);
+ }
+ }
+ default:
+ 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);
+ 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->velocity = (amount * ss->velocity + adjust) / (amount + 1.0f);
+
+ if (fabs (dx) < 0.1f && fabs (ss->velocity) < 0.2f)
+ {
+ ss->velocity = 0.0f;
+ return 0;
+ }
+
+ return 1;
+}
+
+static void
+switchPreparePaintScreen (CompScreen *s,
+ int msSinceLastPaint)
+{
+ SWITCH_SCREEN (s);
+
+ if (ss->grabIndex && ss->moreAdjust)
+ {
+ int steps, m;
+ float amount, chunk;
+
+ amount = msSinceLastPaint * 0.05f * ss->speed;
+ steps = amount / (0.5f * ss->timestep);
+ if (!steps) steps = 1;
+ chunk = amount / (float) steps;
+
+ while (steps--)
+ {
+ ss->moreAdjust = adjustSwitchVelocity (s);
+ if (!ss->moreAdjust)
+ {
+ ss->pos += ss->move;
+ ss->move = 0;
+ break;
+ }
+
+ m = ss->velocity * chunk;
+
+ 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;
+ }
+ }
+
+ UNWRAP (ss, s, preparePaintScreen);
+ (*s->preparePaintScreen) (s, msSinceLastPaint);
+ WRAP (ss, s, preparePaintScreen, switchPreparePaintScreen);
+}
+
+static void
+switchDonePaintScreen (CompScreen *s)
+{
+ SWITCH_SCREEN (s);
+
+ if (ss->grabIndex && ss->moreAdjust)
+ {
+ 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,
+ unsigned int mask,
+ int x,
+ int y,
+ int x1,
+ int x2)
+{
+ WindowPaintAttrib sAttrib = *attrib;
+ int dx, dy;
+ int wx, wy;
+ float width, height;
+ REGION reg;
+
+ width = WIDTH - (SPACE << 1);
+ height = HEIGHT - (SPACE << 1);
+
+ if (w->width > width)
+ sAttrib.xScale = width / w->width;
+ else
+ sAttrib.xScale = 1.0f;
+
+ if (w->height > height)
+ sAttrib.yScale = height / w->height;
+ else
+ sAttrib.yScale = 1.0f;
+
+ if (sAttrib.xScale < sAttrib.yScale)
+ sAttrib.yScale = sAttrib.xScale;
+ else
+ sAttrib.xScale = sAttrib.yScale;
+
+ width = w->width * sAttrib.xScale;
+ height = w->height * sAttrib.yScale;
+
+ wx = x + SPACE + ((WIDTH - (SPACE << 1)) - width) / 2;
+ wy = y + SPACE + ((HEIGHT - (SPACE << 1)) - height) / 2;
+
+ if (!w->texture.pixmap)
+ bindWindow (w);
+
+ dx = wx - w->attrib.x;
+ dy = wy - w->attrib.y;
+
+ moveWindow (w, dx, dy, FALSE);
+
+ mask = mask | PAINT_WINDOW_TRANSFORMED_MASK;
+ if (w->alpha)
+ mask |= PAINT_WINDOW_TRANSLUCENT_MASK;
+ else if (sAttrib.opacity == OPAQUE)
+ mask &= ~PAINT_WINDOW_TRANSLUCENT_MASK;
+
+ reg.rects = &reg.extents;
+ reg.numRects = 1;
+
+ reg.extents.y1 = MINSHORT;
+ reg.extents.y2 = MAXSHORT;
+ reg.extents.x1 = wx + (x1 - wx) / sAttrib.xScale;
+ reg.extents.x2 = wx + (x2 - wx) / sAttrib.xScale;
+
+ w->vCount = 0;
+ addWindowGeometry (w, &w->matrix, 1, w->region, &reg);
+ if (w->vCount)
+ drawWindowTexture (w, &w->texture, &sAttrib, mask);
+
+ moveWindow (w, -dx, -dy, FALSE);
+}
+
+static Bool
+switchPaintWindow (CompWindow *w,
+ const WindowPaintAttrib *attrib,
+ Region region,
+ unsigned int mask)
+{
+ CompScreen *s = w->screen;
+ Bool status;
+
+ SWITCH_SCREEN (s);
+
+ if (ss->grabIndex && w->id != ss->selectedWindow)
+ {
+ if (w->id == ss->popupWindow)
+ {
+ int x, y, x1, x2, cx, i;
+
+ if (mask & PAINT_WINDOW_SOLID_MASK)
+ return FALSE;
+
+ UNWRAP (ss, s, paintWindow);
+ status = (*s->paintWindow) (w, attrib, region, mask);
+ WRAP (ss, s, paintWindow, switchPaintWindow);
+
+ x1 = w->attrib.x + BORDER + SPACE;
+ x2 = w->attrib.x + w->width - BORDER - SPACE;
+
+ x = x1 + ss->pos;
+ y = w->attrib.y + BORDER + SPACE;
+
+ for (i = 0; i < ss->nWindows; i++)
+ {
+ if (x + WIDTH > x1)
+ switchPaintThumb (ss->windows[i], attrib, mask,
+ x, y, x1, x2);
+
+ x += WIDTH;
+ }
+
+ for (i = 0; i < ss->nWindows; i++)
+ {
+ if (x > x2)
+ break;
+
+ switchPaintThumb (ss->windows[i], attrib, mask,
+ x, y, x1, x2);
+
+ x += WIDTH;
+ }
+
+ cx = w->attrib.x + (w->width >> 1);
+
+ glColor4us (0, 0, 0, attrib->opacity);
+ glPushMatrix ();
+ glTranslatef (cx, y, 0.0f);
+ glVertexPointer (2, GL_FLOAT, 0, _boxVertices);
+ glDrawArrays (GL_QUADS, 0, 16);
+ glPopMatrix ();
+ }
+ else
+ {
+ WindowPaintAttrib sAttrib = *attrib;
+
+ sAttrib.brightness = (2 * sAttrib.brightness) / 3;
+
+ if (ss->wMask & w->type)
+ sAttrib.opacity >>= 1;
+
+ UNWRAP (ss, s, paintWindow);
+ status = (*s->paintWindow) (w, &sAttrib, region, mask);
+ WRAP (ss, s, paintWindow, switchPaintWindow);
+ }
+ }
+ else
+ {
+ UNWRAP (ss, s, paintWindow);
+ status = (*s->paintWindow) (w, attrib, 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)
+ {
+ if (initial)
+ {
+ if (isSwitchWin (w))
+ {
+ ss->lastActiveNum = w->screen->activeNum;
+ ss->selectedWindow = w->screen->display->activeWindow;
+
+ switchUpdateWindowList (w->screen,
+ switchCountWindows (w->screen));
+ }
+ }
+ else if (!ss->moreAdjust)
+ {
+ if (isSwitchWin (w))
+ {
+ CompWindow *popup;
+
+ popup = findWindowAtScreen (w->screen, ss->popupWindow);
+ if (popup)
+ addWindowDamage (popup);
+
+ }
+ }
+ }
+
+ UNWRAP (ss, w->screen, damageWindowRect);
+ status = (*w->screen->damageWindowRect) (w, initial, rect);
+ WRAP (ss, w->screen, damageWindowRect, switchDamageWindowRect);
+
+ return status;
+}
+
+static Bool
+switchInitDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ SwitchDisplay *sd;
+
+ sd = malloc (sizeof (SwitchDisplay));
+ if (!sd)
+ return FALSE;
+
+ sd->screenPrivateIndex = allocateScreenPrivateIndex (d);
+ if (sd->screenPrivateIndex < 0)
+ {
+ free (sd);
+ return FALSE;
+ }
+
+ sd->popupWinAtom = XInternAtom (d->display, POPUP_WIN_PROP, 0);
+ sd->selectWinAtom = XInternAtom (d->display, SELECT_WIN_PROP, 0);
+
+ WRAP (sd, d, handleEvent, switchHandleEvent);
+
+ d->privates[displayPrivateIndex].ptr = sd;
+
+ return TRUE;
+}
+
+static void
+switchFiniDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ SWITCH_DISPLAY (d);
+
+ freeScreenPrivateIndex (d, sd->screenPrivateIndex);
+
+ UNWRAP (sd, d, handleEvent);
+
+ free (sd);
+}
+
+static Bool
+switchInitScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ SwitchScreen *ss;
+
+ SWITCH_DISPLAY (s->display);
+
+ ss = malloc (sizeof (SwitchScreen));
+ if (!ss)
+ return FALSE;
+
+ ss->pid = 0;
+ ss->popupWindow = None;
+
+ ss->selectedWindow = None;
+ ss->lastActiveNum = 0;
+
+ ss->windows = 0;
+ ss->nWindows = 0;
+ ss->windowsSize = 0;
+
+ ss->pos = ss->move = 0;
+
+ ss->grabIndex = 0;
+
+ ss->speed = SWITCH_SPEED_DEFAULT;
+ ss->timestep = SWITCH_TIMESTEP_DEFAULT;
+
+ ss->moreAdjust = 0;
+
+ ss->velocity = 0.0f;
+
+ switchScreenInitOptions (ss, s->display->display);
+
+ addScreenBinding (s, &ss->opt[SWITCH_SCREEN_OPTION_INITIATE].value.bind);
+
+ WRAP (ss, s, preparePaintScreen, switchPreparePaintScreen);
+ WRAP (ss, s, donePaintScreen, switchDonePaintScreen);
+ WRAP (ss, s, paintWindow, switchPaintWindow);
+ WRAP (ss, s, damageWindowRect, switchDamageWindowRect);
+
+ s->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, paintWindow);
+ UNWRAP (ss, s, damageWindowRect);
+
+ if (ss->windowsSize)
+ free (ss->windows);
+
+ free (ss);
+}
+
+static Bool
+switchInit (CompPlugin *p)
+{
+ displayPrivateIndex = allocateDisplayPrivateIndex ();
+ if (displayPrivateIndex < 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+switchFini (CompPlugin *p)
+{
+ if (displayPrivateIndex >= 0)
+ freeDisplayPrivateIndex (displayPrivateIndex);
+}
+
+CompPluginVTable switchVTable = {
+ "switcher",
+ "Application Switcher",
+ "Application Switcher",
+ switchInit,
+ switchFini,
+ switchInitDisplay,
+ switchFiniDisplay,
+ switchInitScreen,
+ switchFiniScreen,
+ 0, /* InitWindow */
+ 0, /* FiniWindow */
+ 0, /* GetDisplayOptions */
+ 0, /* SetDisplayOption */
+ switchGetScreenOptions,
+ switchSetScreenOption,
+ NULL,
+ 0
+};
+
+CompPluginVTable *
+getCompPluginInfo (void)
+{
+ return &switchVTable;
+}
diff --git a/plugins/wobbly.c b/plugins/wobbly.c
new file mode 100644
index 00000000..4887a5c8
--- /dev/null
+++ b/plugins/wobbly.c
@@ -0,0 +1,2593 @@
+/*
+ * 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.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;
+ Vector scale;
+ Bool transformed;
+ Point topLeft;
+ Point bottomRight;
+ unsigned int edgeMask;
+ unsigned int snapCnt[4];
+} Model;
+
+#define WOBBLY_FRICTION_DEFAULT 3.0f
+#define WOBBLY_FRICTION_MIN 0.1f
+#define WOBBLY_FRICTION_MAX 10.0f
+#define WOBBLY_FRICTION_PRECISION 0.1f
+
+#define WOBBLY_SPRING_K_DEFAULT 8.0f
+#define WOBBLY_SPRING_K_MIN 0.1f
+#define WOBBLY_SPRING_K_MAX 10.0f
+#define WOBBLY_SPRING_K_PRECISION 0.1f
+
+#define WOBBLY_GRID_RESOLUTION_DEFAULT 8
+#define WOBBLY_GRID_RESOLUTION_MIN 1
+#define WOBBLY_GRID_RESOLUTION_MAX 64
+
+#define WOBBLY_MIN_GRID_SIZE_DEFAULT 8
+#define WOBBLY_MIN_GRID_SIZE_MIN 4
+#define WOBBLY_MIN_GRID_SIZE_MAX 128
+
+#define WOBBLY_WOBBLE_ON_GRAB_DEFAULT FALSE
+
+#define WOBBLY_WOBBLE_ON_MOVE_DEFAULT TRUE
+
+#define WOBBLY_WOBBLE_ON_RESIZE_DEFAULT FALSE
+
+typedef enum {
+ WobblyEffectNone = 0,
+ WobblyEffectShiver
+} WobblyEffect;
+
+static char *effectName[] = {
+ "None",
+ "Shiver"
+};
+
+static WobblyEffect effectType[] = {
+ WobblyEffectNone,
+ WobblyEffectShiver
+};
+
+#define NUM_EFFECT (sizeof (effectType) / sizeof (effectType[0]))
+
+#define WOBBLY_MAP_DEFAULT (effectName[0])
+#define WOBBLY_FOCUS_DEFAULT (effectName[0])
+
+static char *winType[] = {
+ "Toolbar",
+ "Menu",
+ "Utility",
+ "Splash",
+ "Dialog",
+ "Normal",
+ "Unknown"
+};
+#define N_WIN_TYPE (sizeof (winType) / sizeof (winType[0]))
+
+#define WOBBLY_SNAP_KEY_DEFAULT "Control_L"
+#define WOBBLY_SNAP_MODIFIERS_DEFAULT (CompPressMask | ControlMask)
+
+static int displayPrivateIndex;
+
+typedef struct _WobblyDisplay {
+ int screenPrivateIndex;
+ HandleEventProc handleEvent;
+} 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_WINDOW_TYPE 6
+#define WOBBLY_SCREEN_OPTION_WOBBLE_ON_GRAB 7
+#define WOBBLY_SCREEN_OPTION_WOBBLE_ON_MOVE 8
+#define WOBBLY_SCREEN_OPTION_WOBBLE_ON_RESIZE 9
+#define WOBBLY_SCREEN_OPTION_SNAP 10
+#define WOBBLY_SCREEN_OPTION_NUM 11
+
+typedef struct _WobblyScreen {
+ int windowPrivateIndex;
+
+ PreparePaintScreenProc preparePaintScreen;
+ DonePaintScreenProc donePaintScreen;
+ PaintScreenProc paintScreen;
+ PaintWindowProc paintWindow;
+ DamageWindowRectProc damageWindowRect;
+ AddWindowGeometryProc addWindowGeometry;
+ DrawWindowGeometryProc drawWindowGeometry;
+ SetWindowScaleProc setWindowScale;
+
+ WindowResizeNotifyProc windowResizeNotify;
+ WindowMoveNotifyProc windowMoveNotify;
+ WindowGrabNotifyProc windowGrabNotify;
+ WindowUngrabNotifyProc windowUngrabNotify;
+
+ CompOption opt[WOBBLY_SCREEN_OPTION_NUM];
+
+ Bool wobblyWindows;
+
+ WobblyEffect mapEffect;
+ WobblyEffect focusEffect;
+
+ unsigned int wMask;
+} WobblyScreen;
+
+#define WobblyInitial (1L << 0)
+#define WobblyForce (1L << 1)
+#define WobblyVelocity (1L << 2)
+
+typedef struct _WobblyWindow {
+ Model *model;
+ int wobbly;
+ Bool grabbed;
+ Bool velocity;
+} WobblyWindow;
+
+#define GET_WOBBLY_DISPLAY(d) \
+ ((WobblyDisplay *) (d)->privates[displayPrivateIndex].ptr)
+
+#define WOBBLY_DISPLAY(d) \
+ WobblyDisplay *wd = GET_WOBBLY_DISPLAY (d)
+
+#define GET_WOBBLY_SCREEN(s, wd) \
+ ((WobblyScreen *) (s)->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)->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 (CompScreen *screen,
+ int *count)
+{
+ WOBBLY_SCREEN (screen);
+
+ *count = NUM_OPTIONS (ws);
+ return ws->opt;
+}
+
+static Bool
+wobblySetScreenOption (CompScreen *screen,
+ char *name,
+ CompOptionValue *value)
+{
+ CompOption *o;
+ int index;
+
+ WOBBLY_SCREEN (screen);
+
+ o = compFindOption (ws->opt, NUM_OPTIONS (ws), name, &index);
+ if (!o)
+ return FALSE;
+
+ switch (index) {
+ case WOBBLY_SCREEN_OPTION_FRICTION:
+ case WOBBLY_SCREEN_OPTION_SPRING_K:
+ if (compSetFloatOption (o, value))
+ return TRUE;
+ break;
+ case WOBBLY_SCREEN_OPTION_GRID_RESOLUTION:
+ if (compSetIntOption (o, value))
+ return TRUE;
+ break;
+ case WOBBLY_SCREEN_OPTION_MIN_GRID_SIZE:
+ if (compSetIntOption (o, value))
+ return TRUE;
+ break;
+ case WOBBLY_SCREEN_OPTION_MAP_EFFECT:
+ if (compSetStringOption (o, value))
+ {
+ int i;
+
+ for (i = 0; i < NUM_EFFECT; i++)
+ {
+ if (strcmp (o->value.s, effectName[i]) == 0)
+ {
+ ws->mapEffect = effectType[i];
+ return TRUE;
+ }
+ }
+ }
+ break;
+ case WOBBLY_SCREEN_OPTION_FOCUS_EFFECT:
+ if (compSetStringOption (o, value))
+ {
+ int i;
+
+ for (i = 0; i < NUM_EFFECT; i++)
+ {
+ if (strcmp (o->value.s, effectName[i]) == 0)
+ {
+ ws->focusEffect = effectType[i];
+ return TRUE;
+ }
+ }
+ }
+ break;
+ case WOBBLY_SCREEN_OPTION_WINDOW_TYPE:
+ if (compSetOptionList (o, value))
+ {
+ ws->wMask = compWindowTypeMaskFromStringList (&o->value);
+ return TRUE;
+ }
+ break;
+ case WOBBLY_SCREEN_OPTION_WOBBLE_ON_GRAB:
+ case WOBBLY_SCREEN_OPTION_WOBBLE_ON_MOVE:
+ case WOBBLY_SCREEN_OPTION_WOBBLE_ON_RESIZE:
+ if (compSetBoolOption (o, value))
+ return TRUE;
+ break;
+ case WOBBLY_SCREEN_OPTION_SNAP:
+ if (value->bind.type == CompBindingTypeButton)
+ return FALSE;
+
+ if (compSetBindingOption (o, value))
+ return TRUE;
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+static void
+wobblyScreenInitOptions (WobblyScreen *ws,
+ Display *display)
+{
+ CompOption *o;
+ int i;
+
+ o = &ws->opt[WOBBLY_SCREEN_OPTION_FRICTION];
+ o->name = "friction";
+ o->shortDesc = "Friction";
+ o->longDesc = "Spring Friction";
+ o->type = CompOptionTypeFloat;
+ o->value.f = WOBBLY_FRICTION_DEFAULT;
+ o->rest.f.min = WOBBLY_FRICTION_MIN;
+ o->rest.f.max = WOBBLY_FRICTION_MAX;
+ o->rest.f.precision = WOBBLY_FRICTION_PRECISION;
+
+ o = &ws->opt[WOBBLY_SCREEN_OPTION_SPRING_K];
+ o->name = "spring_k";
+ o->shortDesc = "Spring K";
+ o->longDesc = "Spring Konstant";
+ o->type = CompOptionTypeFloat;
+ o->value.f = WOBBLY_SPRING_K_DEFAULT;
+ o->rest.f.min = WOBBLY_SPRING_K_MIN;
+ o->rest.f.max = WOBBLY_SPRING_K_MAX;
+ o->rest.f.precision = WOBBLY_SPRING_K_PRECISION;
+
+ o = &ws->opt[WOBBLY_SCREEN_OPTION_GRID_RESOLUTION];
+ o->name = "grid_resolution";
+ o->shortDesc = "Grid Resolution";
+ o->longDesc = "Vertex Grid Resolution";
+ o->type = CompOptionTypeInt;
+ o->value.i = WOBBLY_GRID_RESOLUTION_DEFAULT;
+ o->rest.i.min = WOBBLY_GRID_RESOLUTION_MIN;
+ o->rest.i.max = WOBBLY_GRID_RESOLUTION_MAX;
+
+ o = &ws->opt[WOBBLY_SCREEN_OPTION_MIN_GRID_SIZE];
+ o->name = "min_grid_size";
+ o->shortDesc = "Minimum Grid Size";
+ o->longDesc = "Minimum Vertex Grid Size";
+ o->type = CompOptionTypeInt;
+ o->value.i = WOBBLY_MIN_GRID_SIZE_DEFAULT;
+ o->rest.i.min = WOBBLY_MIN_GRID_SIZE_MIN;
+ o->rest.i.max = WOBBLY_MIN_GRID_SIZE_MAX;
+
+ o = &ws->opt[WOBBLY_SCREEN_OPTION_MAP_EFFECT];
+ o->name = "map_effect";
+ o->shortDesc = "Map Effect";
+ o->longDesc = "Map Window Effect";
+ o->type = CompOptionTypeString;
+ o->value.s = strdup (WOBBLY_MAP_DEFAULT);
+ o->rest.s.string = effectName;
+ o->rest.s.nString = NUM_EFFECT;
+
+ o = &ws->opt[WOBBLY_SCREEN_OPTION_FOCUS_EFFECT];
+ o->name = "focus_effect";
+ o->shortDesc = "Focus Effect";
+ o->longDesc = "Focus Window Effect";
+ o->type = CompOptionTypeString;
+ o->value.s = strdup (WOBBLY_FOCUS_DEFAULT);
+ o->rest.s.string = effectName;
+ o->rest.s.nString = NUM_EFFECT;
+
+ o = &ws->opt[WOBBLY_SCREEN_OPTION_WINDOW_TYPE];
+ o->name = "window_types";
+ o->shortDesc = "Window Types";
+ o->longDesc = "Window types that should wobbly";
+ o->type = CompOptionTypeList;
+ o->value.list.type = CompOptionTypeString;
+ o->value.list.nValue = N_WIN_TYPE;
+ o->value.list.value = malloc (sizeof (CompOptionValue) * N_WIN_TYPE);
+ for (i = 0; i < N_WIN_TYPE; i++)
+ o->value.list.value[i].s = strdup (winType[i]);
+ o->rest.s.string = windowTypeString;
+ o->rest.s.nString = nWindowTypeString;
+
+ ws->wMask = compWindowTypeMaskFromStringList (&o->value);
+
+ o = &ws->opt[WOBBLY_SCREEN_OPTION_WOBBLE_ON_GRAB];
+ o->name = "wobble_on_grab";
+ o->shortDesc = "Wobble On Grab";
+ o->longDesc = "Wobble when windows are grabbed";
+ o->type = CompOptionTypeBool;
+ o->value.b = WOBBLY_WOBBLE_ON_GRAB_DEFAULT;
+
+ o = &ws->opt[WOBBLY_SCREEN_OPTION_WOBBLE_ON_MOVE];
+ o->name = "wobble_on_move";
+ o->shortDesc = "Wobble On Move";
+ o->longDesc = "Wobble when windows are moved";
+ o->type = CompOptionTypeBool;
+ o->value.b = WOBBLY_WOBBLE_ON_MOVE_DEFAULT;
+
+ o = &ws->opt[WOBBLY_SCREEN_OPTION_WOBBLE_ON_RESIZE];
+ o->name = "wobble_on_resize";
+ o->shortDesc = "Wobble On Resize";
+ o->longDesc = "Wobble when windows are resize";
+ o->type = CompOptionTypeBool;
+ o->value.b = WOBBLY_WOBBLE_ON_RESIZE_DEFAULT;
+
+ o = &ws->opt[WOBBLY_SCREEN_OPTION_SNAP];
+ o->name = "snap";
+ o->shortDesc = "Snap windows";
+ o->longDesc = "Toggle window snapping";
+ o->type = CompOptionTypeBinding;
+ o->value.bind.type = CompBindingTypeKey;
+ o->value.bind.u.key.modifiers = WOBBLY_SNAP_MODIFIERS_DEFAULT;
+ o->value.bind.u.key.keycode =
+ XKeysymToKeycode (display,
+ XStringToKeysym (WOBBLY_SNAP_KEY_DEFAULT));
+}
+
+static void
+findNextWestEdge (CompWindow *w,
+ Object *object)
+{
+ int v, v1, v2;
+ int s, start;
+ int e, end;
+ int x;
+
+ start = -65535.0f;
+ end = 65535.0f;
+
+ v1 = -65535.0f;
+ v2 = 65535.0f;
+
+ x = object->position.x + w->output.left - w->input.left;
+
+ if (x >= w->screen->workArea.x)
+ {
+ CompWindow *p;
+
+ v1 = w->screen->workArea.x;
+
+ for (p = w->screen->windows; p; p = p->next)
+ {
+ if (p->invisible || w == p || p->type != CompWindowTypeNormalMask)
+ continue;
+
+ s = p->attrib.y - p->output.top;
+ e = p->attrib.y + p->height + p->output.bottom;
+
+ 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;
+
+ 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->workArea.x;
+ }
+
+ 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;
+
+ start = -65535.0f;
+ end = 65535.0f;
+
+ v1 = 65535.0f;
+ v2 = -65535.0f;
+
+ x = object->position.x - w->output.right + w->input.right;
+
+ if (x <= w->screen->workArea.x + w->screen->workArea.width)
+ {
+ CompWindow *p;
+
+ v1 = w->screen->workArea.x + w->screen->workArea.width;
+
+ for (p = w->screen->windows; p; p = p->next)
+ {
+ if (p->invisible || w == p || p->type != CompWindowTypeNormalMask)
+ continue;
+
+ s = p->attrib.y - p->output.top;
+ e = p->attrib.y + p->height + p->output.bottom;
+
+ 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;
+
+ 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->workArea.x + w->screen->workArea.width;
+ }
+
+ 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;
+
+ start = -65535.0f;
+ end = 65535.0f;
+
+ v1 = -65535.0f;
+ v2 = 65535.0f;
+
+ y = object->position.y + w->output.top - w->input.top;
+
+ if (y >= w->screen->workArea.y)
+ {
+ CompWindow *p;
+
+ v1 = w->screen->workArea.y;
+
+ for (p = w->screen->windows; p; p = p->next)
+ {
+ if (p->invisible || w == p || p->type != CompWindowTypeNormalMask)
+ continue;
+
+ s = p->attrib.x - p->output.left;
+ e = p->attrib.x + p->width + p->output.right;
+
+ 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;
+
+ 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->workArea.y;
+ }
+
+ 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;
+
+ start = -65535.0f;
+ end = 65535.0f;
+
+ v1 = 65535.0f;
+ v2 = -65535.0f;
+
+ y = object->position.y - w->output.bottom + w->input.bottom;
+
+ if (y <= w->screen->workArea.y + w->screen->workArea.height)
+ {
+ CompWindow *p;
+
+ v1 = w->screen->workArea.y + w->screen->workArea.height;
+
+ for (p = w->screen->windows; p; p = p->next)
+ {
+ if (p->invisible || w == p || p->type != CompWindowTypeNormalMask)
+ continue;
+
+ s = p->attrib.x - p->output.left;
+ e = p->attrib.x + p->width + p->output.right;
+
+ 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;
+
+ 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->workArea.y + w->screen->workArea.height;
+ }
+
+ 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 w, h;
+
+ if (model->anchorObject)
+ model->anchorObject->immobile = FALSE;
+
+ w = (float) width * model->scale.x;
+ h = (float) height * model->scale.y;
+
+ model->anchorObject = &model->objects[GRID_WIDTH *
+ ((GRID_HEIGHT - 1) / 2) +
+ (GRID_WIDTH - 1) / 2];
+ model->anchorObject->position.x = x +
+ ((GRID_WIDTH - 1) / 2 * w) / (float) (GRID_WIDTH - 1);
+ model->anchorObject->position.y = y +
+ ((GRID_HEIGHT - 1) / 2 * h) / (float) (GRID_HEIGHT - 1);
+
+ model->anchorObject->immobile = TRUE;
+}
+
+static void
+modelInitObjects (Model *model,
+ int x,
+ int y,
+ int width,
+ int height)
+{
+ int gridX, gridY, i = 0;
+ float w, h;
+
+ w = (float) width * model->scale.x;
+ h = (float) height * model->scale.y;
+
+ for (gridY = 0; gridY < GRID_HEIGHT; gridY++)
+ {
+ for (gridX = 0; gridX < GRID_WIDTH; gridX++)
+ {
+ objectInit (&model->objects[i],
+ x + (gridX * w) / (float) (GRID_WIDTH - 1),
+ y + (gridY * h) / (float) (GRID_HEIGHT - 1),
+ 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 scale;
+ float w, h;
+
+ w = (float) width * model->scale.x;
+ h = (float) height * model->scale.y;
+
+ 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 hp, hpad, vpad;
+ float w, h;
+
+ model->numSprings = 0;
+
+ w = (float) width * model->scale.x;
+ h = (float) height * model->scale.y;
+
+ hpad = w / (GRID_WIDTH - 1);
+ vpad = h / (GRID_HEIGHT - 1);
+
+ for (gridY = 0; gridY < GRID_HEIGHT; gridY++)
+ {
+ hp = hpad;
+
+ for (gridX = 0; gridX < GRID_WIDTH; gridX++)
+ {
+ if (gridX > 0)
+ modelAddSpring (model,
+ &model->objects[i - 1],
+ &model->objects[i],
+ hp, 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)
+ return 0;
+
+ model->anchorObject = 0;
+ model->numSprings = 0;
+
+ model->steps = 0;
+
+ model->scale.x = 1.0f;
+ model->scale.y = 1.0f;
+
+ model->transformed = FALSE;
+
+ 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 == FALSE ||
+ 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 == FALSE ||
+ 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 == FALSE ||
+ 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 == FALSE ||
+ 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_SCREEN (w->screen);
+ WOBBLY_WINDOW (w);
+
+ if (ww->model)
+ return TRUE;
+
+ if (!(ws->wMask & w->type))
+ return FALSE;
+
+ /* 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))
+ {
+ REGION region;
+ 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;
+
+ region.rects = &region.extents;
+ region.numRects = region.size = 1;
+
+ 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->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;
+
+ moveWindow (w,
+ model->topLeft.x + w->output.left -
+ w->attrib.x,
+ model->topLeft.y + w->output.top -
+ w->attrib.y,
+ TRUE);
+
+ ww->model = model;
+
+ syncWindowPosition (w);
+ }
+
+ 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);
+
+ region.extents.x1 = topLeft.x;
+ region.extents.y1 = topLeft.y;
+ region.extents.x2 = bottomRight.x + 0.5f;
+ region.extents.y2 = bottomRight.y + 0.5f;
+
+ damageScreenRegion (s, &region);
+ }
+ }
+
+ 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)
+ damagePendingOnScreen (s);
+
+ UNWRAP (ws, s, donePaintScreen);
+ (*s->donePaintScreen) (s);
+ WRAP (ws, s, donePaintScreen, wobblyDonePaintScreen);
+}
+
+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 = 2 + nMatrix * 2;
+
+ nVertices = w->vCount;
+ nIndices = w->vCount;
+
+ 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;
+
+ nVertices++;
+
+ if (x == x2)
+ break;
+ }
+
+ if (y == y2)
+ break;
+ }
+
+ pClip++;
+ }
+
+ w->vCount = nIndices;
+ }
+ else
+ {
+ UNWRAP (ws, w->screen, addWindowGeometry);
+ (*w->screen->addWindowGeometry) (w, matrix, nMatrix, region, clip);
+ WRAP (ws, w->screen, addWindowGeometry, wobblyAddWindowGeometry);
+ }
+}
+
+static void
+wobblyDrawWindowGeometry (CompWindow *w)
+{
+ WOBBLY_WINDOW (w);
+
+ if (ww->wobbly)
+ {
+ int texUnit = w->texUnits;
+ int currentTexUnit = 0;
+ int stride = (1 + texUnit) * 2;
+ GLfloat *vertices = w->vertices + (stride - 2);
+
+ stride *= sizeof (GLfloat);
+
+ glVertexPointer (2, GL_FLOAT, stride, vertices);
+
+ while (texUnit--)
+ {
+ if (texUnit != currentTexUnit)
+ {
+ w->screen->clientActiveTexture (GL_TEXTURE0_ARB + texUnit);
+ currentTexUnit = texUnit;
+ }
+ vertices -= 2;
+ glTexCoordPointer (2, GL_FLOAT, stride, vertices);
+ }
+
+ glDrawElements (GL_QUADS, w->vCount, GL_UNSIGNED_SHORT, w->indices);
+ }
+ else
+ {
+ WOBBLY_SCREEN (w->screen);
+
+ UNWRAP (ws, w->screen, drawWindowGeometry);
+ (*w->screen->drawWindowGeometry) (w);
+ WRAP (ws, w->screen, drawWindowGeometry, wobblyDrawWindowGeometry);
+ }
+}
+
+static Bool
+wobblyPaintWindow (CompWindow *w,
+ const WindowPaintAttrib *attrib,
+ Region region,
+ unsigned int mask)
+{
+ Bool status;
+
+ WOBBLY_SCREEN (w->screen);
+ WOBBLY_WINDOW (w);
+
+ if (ww->wobbly)
+ {
+ if (mask & PAINT_WINDOW_SOLID_MASK)
+ return FALSE;
+
+ mask |= PAINT_WINDOW_TRANSFORMED_MASK;
+ }
+
+ UNWRAP (ws, w->screen, paintWindow);
+ status = (*w->screen->paintWindow) (w, attrib, region, mask);
+ WRAP (ws, w->screen, paintWindow, wobblyPaintWindow);
+
+ return status;
+}
+
+static void
+wobblyHandleEvent (CompDisplay *d,
+ XEvent *event)
+{
+ Window activeWindow = 0;
+ CompWindow *w;
+ CompScreen *s;
+
+ WOBBLY_DISPLAY (d);
+
+ switch (event->type) {
+ case PropertyNotify:
+ if (event->xproperty.atom == d->winActiveAtom)
+ activeWindow = d->activeWindow;
+ break;
+ case MapNotify:
+ w = findWindowAtDisplay (d, event->xunmap.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;
+ case KeyPress:
+ s = findScreenAtDisplay (d, event->xkey.root);
+ if (s)
+ {
+ WOBBLY_SCREEN (s);
+
+#define EV_SNAP_KEY(opt, event) \
+ ((opt)->value.bind.type == CompBindingTypeKey && \
+ (opt)->value.bind.u.key.keycode == (event)->xkey.keycode)
+
+ if (EV_SNAP_KEY (&ws->opt[WOBBLY_SCREEN_OPTION_SNAP], event))
+ {
+ for (w = s->windows; w; w = w->next)
+ {
+ WOBBLY_WINDOW (w);
+
+ if (ww->grabbed && ww->model)
+ modelUpdateSnapping (w, ww->model);
+ }
+ }
+ }
+ break;
+ case KeyRelease:
+ s = findScreenAtDisplay (d, event->xkey.root);
+ if (s)
+ {
+ WOBBLY_SCREEN (s);
+
+ if (EV_SNAP_KEY (&ws->opt[WOBBLY_SCREEN_OPTION_SNAP], event))
+ {
+ WOBBLY_SCREEN (s);
+
+ for (w = s->windows; w; w = w->next)
+ {
+ WOBBLY_WINDOW (w);
+
+ if (ww->grabbed && ww->model)
+ {
+ if (modelDisableSnapping (w, ww->model))
+ {
+ ww->wobbly |= WobblyInitial;
+ ws->wobblyWindows |= ww->wobbly;
+
+ damagePendingOnScreen (w->screen);
+ }
+ }
+ }
+ }
+ }
+ default:
+ break;
+ }
+
+ UNWRAP (wd, d, handleEvent);
+ (*d->handleEvent) (d, event);
+ WRAP (wd, d, handleEvent, wobblyHandleEvent);
+
+ switch (event->type) {
+ case PropertyNotify:
+ if (event->xproperty.atom == d->winActiveAtom)
+ {
+ if (d->activeWindow != activeWindow)
+ {
+ CompWindow *w;
+
+ w = findWindowAtDisplay (d, d->activeWindow);
+ if (w && isWobblyWin (w))
+ {
+ WOBBLY_WINDOW (w);
+ WOBBLY_SCREEN (w->screen);
+
+ if (ws->focusEffect && wobblyEnsureModel (w))
+ {
+ switch (ws->focusEffect) {
+ case WobblyEffectShiver:
+ 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);
+ }
+ }
+ }
+ }
+ default:
+ break;
+ }
+}
+
+static Bool
+wobblyDamageWindowRect (CompWindow *w,
+ Bool initial,
+ BoxPtr rect)
+{
+ Bool status;
+
+ WOBBLY_SCREEN (w->screen);
+
+ if (!initial)
+ {
+ WOBBLY_WINDOW (w);
+
+ if (ww->wobbly == WobblyForce)
+ {
+ REGION region;
+
+ region.rects = &region.extents;
+ region.numRects = region.size = 1;
+
+ region.extents.x1 = ww->model->topLeft.x;
+ region.extents.y1 = ww->model->topLeft.y;
+ region.extents.x2 = ww->model->bottomRight.x + 0.5f;
+ region.extents.y2 = ww->model->bottomRight.y + 0.5f;
+
+ damageScreenRegion (w->screen, &region);
+
+ return TRUE;
+ }
+ }
+
+ UNWRAP (ws, w->screen, damageWindowRect);
+ status = (*w->screen->damageWindowRect) (w, initial, rect);
+ WRAP (ws, w->screen, damageWindowRect, wobblyDamageWindowRect);
+
+ if (initial)
+ {
+ if (isWobblyWin (w) && (w->type & ~CompWindowTypeNormalMask))
+ {
+ WOBBLY_WINDOW (w);
+ WOBBLY_SCREEN (w->screen);
+
+ if (ws->mapEffect && wobblyEnsureModel (w))
+ {
+ switch (ws->mapEffect) {
+ case WobblyEffectShiver:
+ 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
+wobblySetWindowScale (CompWindow *w,
+ float xScale,
+ float yScale)
+{
+ WOBBLY_WINDOW (w);
+ WOBBLY_SCREEN (w->screen);
+
+ UNWRAP (ws, w->screen, setWindowScale);
+ (*w->screen->setWindowScale) (w, xScale, yScale);
+ WRAP (ws, w->screen, setWindowScale, wobblySetWindowScale);
+
+ if (wobblyEnsureModel (w))
+ {
+ if (ww->model->scale.x != xScale ||
+ ww->model->scale.y != yScale)
+ {
+ ww->model->scale.x = xScale;
+ ww->model->scale.y = yScale;
+
+ 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));
+
+ /*
+ ww->wobbly |= WobblyInitial;
+ ws->wobblyWindows |= ww->wobbly;
+ */
+ }
+
+ if (ww->model->scale.x != 1.0f || ww->model->scale.y != 1.0f)
+ ww->model->transformed = 1;
+ else
+ ww->model->transformed = 0;
+ }
+}
+
+static void
+wobblyWindowResizeNotify (CompWindow *w)
+{
+ WOBBLY_SCREEN (w->screen);
+ 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));
+ }
+
+ UNWRAP (ws, w->screen, windowResizeNotify);
+ (*w->screen->windowResizeNotify) (w);
+ WRAP (ws, w->screen, windowResizeNotify, wobblyWindowResizeNotify);
+}
+
+static void
+wobblyWindowMoveNotify (CompWindow *w,
+ int dx,
+ int dy)
+{
+ WOBBLY_SCREEN (w->screen);
+ WOBBLY_WINDOW (w);
+
+ if (ww->model)
+ {
+ if (ww->grabbed)
+ {
+ 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);
+ WRAP (ws, w->screen, windowMoveNotify, wobblyWindowMoveNotify);
+}
+
+static void
+wobblyWindowGrabNotify (CompWindow *w,
+ int x,
+ int y,
+ unsigned int state,
+ unsigned int mask)
+{
+ WOBBLY_SCREEN (w->screen);
+
+ if ((mask & CompWindowGrabButtonMask) &&
+ ws->opt[WOBBLY_SCREEN_OPTION_WOBBLE_ON_MOVE].value.b &&
+ isWobblyWin (w))
+ {
+ WOBBLY_WINDOW (w);
+
+ if (ww->model || wobblyEnsureModel (w))
+ {
+ Spring *s;
+ int i;
+
+ 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)
+ {
+ CompBinding *bind;
+
+ bind = &ws->opt[WOBBLY_SCREEN_OPTION_SNAP].value.bind;
+
+ modelDisableSnapping (w, ww->model);
+ if ((state & bind->u.key.modifiers) == bind->u.key.modifiers)
+ modelUpdateSnapping (w, ww->model);
+ }
+
+ if (ws->opt[WOBBLY_SCREEN_OPTION_WOBBLE_ON_GRAB].value.b)
+ {
+ 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 (ww->grabbed)
+ {
+ if (ww->model)
+ {
+ if (ww->model->anchorObject)
+ ww->model->anchorObject->immobile = FALSE;
+
+ ww->model->anchorObject = NULL;
+
+ 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
+wobblyPaintScreen (CompScreen *s,
+ const ScreenPaintAttrib *sAttrib,
+ Region region,
+ unsigned int mask)
+{
+ Bool status;
+
+ WOBBLY_SCREEN (s);
+
+ if (ws->wobblyWindows)
+ mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS_MASK;
+
+ UNWRAP (ws, s, paintScreen);
+ status = (*s->paintScreen) (s, sAttrib, region, mask);
+ WRAP (ws, s, paintScreen, wobblyPaintScreen);
+
+ return status;
+}
+
+static Bool
+wobblyInitDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ WobblyDisplay *wd;
+
+ wd = malloc (sizeof (WobblyDisplay));
+ if (!wd)
+ return FALSE;
+
+ wd->screenPrivateIndex = allocateScreenPrivateIndex (d);
+ if (wd->screenPrivateIndex < 0)
+ {
+ free (wd);
+ return FALSE;
+ }
+
+ WRAP (wd, d, handleEvent, wobblyHandleEvent);
+
+ d->privates[displayPrivateIndex].ptr = wd;
+
+ return TRUE;
+}
+
+static void
+wobblyFiniDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ WOBBLY_DISPLAY (d);
+
+ freeScreenPrivateIndex (d, wd->screenPrivateIndex);
+
+ UNWRAP (wd, d, handleEvent);
+
+ free (wd);
+}
+
+static Bool
+wobblyInitScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ WobblyScreen *ws;
+
+ WOBBLY_DISPLAY (s->display);
+
+ ws = malloc (sizeof (WobblyScreen));
+ if (!ws)
+ return FALSE;
+
+ ws->windowPrivateIndex = allocateWindowPrivateIndex (s);
+ if (ws->windowPrivateIndex < 0)
+ {
+ free (ws);
+ return FALSE;
+ }
+
+ ws->wobblyWindows = FALSE;
+
+ ws->mapEffect = WobblyEffectShiver;
+ ws->focusEffect = WobblyEffectNone;
+
+ wobblyScreenInitOptions (ws, s->display->display);
+
+ WRAP (ws, s, preparePaintScreen, wobblyPreparePaintScreen);
+ WRAP (ws, s, donePaintScreen, wobblyDonePaintScreen);
+ WRAP (ws, s, paintScreen, wobblyPaintScreen);
+ WRAP (ws, s, paintWindow, wobblyPaintWindow);
+ WRAP (ws, s, damageWindowRect, wobblyDamageWindowRect);
+ WRAP (ws, s, addWindowGeometry, wobblyAddWindowGeometry);
+ WRAP (ws, s, drawWindowGeometry, wobblyDrawWindowGeometry);
+ WRAP (ws, s, setWindowScale, wobblySetWindowScale);
+ WRAP (ws, s, windowResizeNotify, wobblyWindowResizeNotify);
+ WRAP (ws, s, windowMoveNotify, wobblyWindowMoveNotify);
+ WRAP (ws, s, windowGrabNotify, wobblyWindowGrabNotify);
+ WRAP (ws, s, windowUngrabNotify, wobblyWindowUngrabNotify);
+
+ s->privates[wd->screenPrivateIndex].ptr = ws;
+
+ return TRUE;
+}
+
+static void
+wobblyFiniScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ WOBBLY_SCREEN (s);
+
+ freeWindowPrivateIndex (s, ws->windowPrivateIndex);
+
+ free (ws->opt[WOBBLY_SCREEN_OPTION_MAP_EFFECT].value.s);
+ free (ws->opt[WOBBLY_SCREEN_OPTION_FOCUS_EFFECT].value.s);
+
+ UNWRAP (ws, s, preparePaintScreen);
+ UNWRAP (ws, s, donePaintScreen);
+ UNWRAP (ws, s, paintScreen);
+ UNWRAP (ws, s, paintWindow);
+ UNWRAP (ws, s, damageWindowRect);
+ UNWRAP (ws, s, addWindowGeometry);
+ UNWRAP (ws, s, drawWindowGeometry);
+ UNWRAP (ws, s, setWindowScale);
+ UNWRAP (ws, s, windowResizeNotify);
+ UNWRAP (ws, s, windowMoveNotify);
+ UNWRAP (ws, s, windowGrabNotify);
+ UNWRAP (ws, s, windowUngrabNotify);
+
+ 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;
+
+ w->privates[ws->windowPrivateIndex].ptr = ww;
+
+ return TRUE;
+}
+
+static void
+wobblyFiniWindow (CompPlugin *p,
+ CompWindow *w)
+{
+ WOBBLY_WINDOW (w);
+
+ if (ww->model)
+ {
+ free (ww->model->objects);
+ free (ww->model);
+ }
+
+ free (ww);
+}
+
+static Bool
+wobblyInit (CompPlugin *p)
+{
+ displayPrivateIndex = allocateDisplayPrivateIndex ();
+ if (displayPrivateIndex < 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+wobblyFini (CompPlugin *p)
+{
+ if (displayPrivateIndex >= 0)
+ freeDisplayPrivateIndex (displayPrivateIndex);
+}
+
+CompPluginDep wobblyDeps[] = {
+ { CompPluginRuleBefore, "fade" },
+ { CompPluginRuleBefore, "cube" },
+ { CompPluginRuleBefore, "expose" }
+};
+
+CompPluginVTable wobblyVTable = {
+ "wobbly",
+ "Wobbly Windows",
+ "Use spring model for wobbly window effect",
+ wobblyInit,
+ wobblyFini,
+ wobblyInitDisplay,
+ wobblyFiniDisplay,
+ wobblyInitScreen,
+ wobblyFiniScreen,
+ wobblyInitWindow,
+ wobblyFiniWindow,
+ 0, /* GetDisplayOptions */
+ 0, /* SetDisplayOption */
+ wobblyGetScreenOptions,
+ wobblySetScreenOption,
+ wobblyDeps,
+ sizeof (wobblyDeps) / sizeof (wobblyDeps[0])
+};
+
+CompPluginVTable *
+getCompPluginInfo (void)
+{
+ return &wobblyVTable;
+}
diff --git a/plugins/zoom.c b/plugins/zoom.c
new file mode 100644
index 00000000..50c69cc4
--- /dev/null
+++ b/plugins/zoom.c
@@ -0,0 +1,823 @@
+/*
+ * 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 <compiz.h>
+
+#define ZOOM_POINTER_INVERT_Y_DEFAULT FALSE
+
+#define ZOOM_POINTER_SENSITIVITY_DEFAULT 1.0f
+#define ZOOM_POINTER_SENSITIVITY_MIN 0.01f
+#define ZOOM_POINTER_SENSITIVITY_MAX 100.0f
+#define ZOOM_POINTER_SENSITIVITY_PRECISION 0.01f
+
+#define ZOOM_POINTER_SENSITIVITY_FACTOR 0.0015f
+
+#define ZOOM_INITIATE_BUTTON_DEFAULT Button3
+#define ZOOM_INITIATE_MODIFIERS_DEFAULT (CompPressMask | CompSuperMask)
+
+#define ZOOM_TERMINATE_BUTTON_DEFAULT Button3
+#define ZOOM_TERMINATE_MODIFIERS_DEFAULT CompReleaseMask
+
+#define ZOOM_IN_BUTTON_DEFAULT Button4
+#define ZOOM_IN_MODIFIERS_DEFAULT (CompPressMask | CompSuperMask)
+
+#define ZOOM_OUT_BUTTON_DEFAULT Button5
+#define ZOOM_OUT_MODIFIERS_DEFAULT (CompPressMask | CompSuperMask)
+
+#define ZOOM_SPEED_DEFAULT 1.5f
+#define ZOOM_SPEED_MIN 0.1f
+#define ZOOM_SPEED_MAX 50.0f
+#define ZOOM_SPEED_PRECISION 0.1f
+
+#define ZOOM_TIMESTEP_DEFAULT 1.2f
+#define ZOOM_TIMESTEP_MIN 0.1f
+#define ZOOM_TIMESTEP_MAX 50.0f
+#define ZOOM_TIMESTEP_PRECISION 0.1f
+
+static int displayPrivateIndex;
+
+typedef struct _ZoomDisplay {
+ int screenPrivateIndex;
+ HandleEventProc handleEvent;
+} ZoomDisplay;
+
+#define ZOOM_SCREEN_OPTION_POINTER_INVERT_Y 0
+#define ZOOM_SCREEN_OPTION_POINTER_SENSITIVITY 1
+#define ZOOM_SCREEN_OPTION_INITIATE 2
+#define ZOOM_SCREEN_OPTION_TERMINATE 3
+#define ZOOM_SCREEN_OPTION_IN 4
+#define ZOOM_SCREEN_OPTION_OUT 5
+#define ZOOM_SCREEN_OPTION_SPEED 6
+#define ZOOM_SCREEN_OPTION_TIMESTEP 7
+#define ZOOM_SCREEN_OPTION_NUM 8
+
+typedef struct _ZoomScreen {
+ PreparePaintScreenProc preparePaintScreen;
+ DonePaintScreenProc donePaintScreen;
+ PaintScreenProc paintScreen;
+ SetScreenOptionForPluginProc setScreenOptionForPlugin;
+
+ CompOption opt[ZOOM_SCREEN_OPTION_NUM];
+
+ Bool pointerInvertY;
+ float pointerSensitivity;
+
+ float speed;
+ float timestep;
+
+ int grabIndex;
+
+ GLfloat currentZoom;
+ GLfloat newZoom;
+
+ GLfloat xVelocity;
+ GLfloat yVelocity;
+ GLfloat zVelocity;
+
+ GLfloat xTranslate;
+ GLfloat yTranslate;
+
+ GLfloat xtrans;
+ GLfloat ytrans;
+ GLfloat ztrans;
+
+ int prevPointerX;
+ int prevPointerY;
+ XPoint savedPointer;
+ Bool grabbed;
+
+ float maxTranslate;
+} ZoomScreen;
+
+#define GET_ZOOM_DISPLAY(d) \
+ ((ZoomDisplay *) (d)->privates[displayPrivateIndex].ptr)
+
+#define ZOOM_DISPLAY(d) \
+ ZoomDisplay *zd = GET_ZOOM_DISPLAY (d)
+
+#define GET_ZOOM_SCREEN(s, zd) \
+ ((ZoomScreen *) (s)->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 (CompScreen *screen,
+ int *count)
+{
+ ZOOM_SCREEN (screen);
+
+ *count = NUM_OPTIONS (zs);
+ return zs->opt;
+}
+
+static Bool
+zoomSetScreenOption (CompScreen *screen,
+ char *name,
+ CompOptionValue *value)
+{
+ CompOption *o;
+ int index;
+
+ ZOOM_SCREEN (screen);
+
+ o = compFindOption (zs->opt, NUM_OPTIONS (zs), name, &index);
+ if (!o)
+ return FALSE;
+
+ switch (index) {
+ case ZOOM_SCREEN_OPTION_POINTER_INVERT_Y:
+ if (compSetBoolOption (o, value))
+ {
+ zs->pointerInvertY = o->value.b;
+ return TRUE;
+ }
+ break;
+ case ZOOM_SCREEN_OPTION_POINTER_SENSITIVITY:
+ if (compSetFloatOption (o, value))
+ {
+ zs->pointerSensitivity = o->value.f *
+ ZOOM_POINTER_SENSITIVITY_FACTOR;
+ return TRUE;
+ }
+ break;
+ case ZOOM_SCREEN_OPTION_INITIATE:
+ case ZOOM_SCREEN_OPTION_IN:
+ if (addScreenBinding (screen, &value->bind))
+ {
+ removeScreenBinding (screen, &o->value.bind);
+
+ if (compSetBindingOption (o, value))
+ return TRUE;
+ }
+ break;
+ case ZOOM_SCREEN_OPTION_TERMINATE:
+ case ZOOM_SCREEN_OPTION_OUT:
+ if (compSetBindingOption (o, value))
+ return TRUE;
+ break;
+ case ZOOM_SCREEN_OPTION_SPEED:
+ if (compSetFloatOption (o, value))
+ {
+ zs->speed = o->value.f;
+ return TRUE;
+ }
+ break;
+ case ZOOM_SCREEN_OPTION_TIMESTEP:
+ if (compSetFloatOption (o, value))
+ {
+ zs->timestep = o->value.f;
+ return TRUE;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+static void
+zoomScreenInitOptions (ZoomScreen *zs,
+ Display *display)
+{
+ CompOption *o;
+
+ o = &zs->opt[ZOOM_SCREEN_OPTION_POINTER_INVERT_Y];
+ o->name = "invert_y";
+ o->shortDesc = "Pointer Invert Y";
+ o->longDesc = "Invert Y axis for pointer movement";
+ o->type = CompOptionTypeBool;
+ o->value.b = ZOOM_POINTER_INVERT_Y_DEFAULT;
+
+ o = &zs->opt[ZOOM_SCREEN_OPTION_POINTER_SENSITIVITY];
+ o->name = "sensitivity";
+ o->shortDesc = "Pointer Sensitivity";
+ o->longDesc = "Sensitivity of pointer movement";
+ o->type = CompOptionTypeFloat;
+ o->value.f = ZOOM_POINTER_SENSITIVITY_DEFAULT;
+ o->rest.f.min = ZOOM_POINTER_SENSITIVITY_MIN;
+ o->rest.f.max = ZOOM_POINTER_SENSITIVITY_MAX;
+ o->rest.f.precision = ZOOM_POINTER_SENSITIVITY_PRECISION;
+
+ o = &zs->opt[ZOOM_SCREEN_OPTION_INITIATE];
+ o->name = "initiate";
+ o->shortDesc = "Initiate";
+ o->longDesc = "Zoom In";
+ o->type = CompOptionTypeBinding;
+ o->value.bind.type = CompBindingTypeButton;
+ o->value.bind.u.button.modifiers = ZOOM_INITIATE_MODIFIERS_DEFAULT;
+ o->value.bind.u.button.button = ZOOM_INITIATE_BUTTON_DEFAULT;
+
+ o = &zs->opt[ZOOM_SCREEN_OPTION_TERMINATE];
+ o->name = "terminate";
+ o->shortDesc = "Terminate";
+ o->longDesc = "Zoom to Normal View";
+ o->type = CompOptionTypeBinding;
+ o->value.bind.type = CompBindingTypeButton;
+ o->value.bind.u.button.modifiers = ZOOM_TERMINATE_MODIFIERS_DEFAULT;
+ o->value.bind.u.button.button = ZOOM_TERMINATE_BUTTON_DEFAULT;
+
+ o = &zs->opt[ZOOM_SCREEN_OPTION_IN];
+ o->name = "zoom_in";
+ o->shortDesc = "Zoom In";
+ o->longDesc = "Zoom In";
+ o->type = CompOptionTypeBinding;
+ o->value.bind.type = CompBindingTypeButton;
+ o->value.bind.u.button.modifiers = ZOOM_IN_MODIFIERS_DEFAULT;
+ o->value.bind.u.button.button = ZOOM_IN_BUTTON_DEFAULT;
+
+ o = &zs->opt[ZOOM_SCREEN_OPTION_OUT];
+ o->name = "zoom_out";
+ o->shortDesc = "Zoom Out";
+ o->longDesc = "Zoom Out";
+ o->type = CompOptionTypeBinding;
+ o->value.bind.type = CompBindingTypeButton;
+ o->value.bind.u.button.modifiers = ZOOM_OUT_MODIFIERS_DEFAULT;
+ o->value.bind.u.button.button = ZOOM_OUT_BUTTON_DEFAULT;
+
+ o = &zs->opt[ZOOM_SCREEN_OPTION_SPEED];
+ o->name = "speed";
+ o->shortDesc = "Speed";
+ o->longDesc = "Zoom Speed";
+ o->type = CompOptionTypeFloat;
+ o->value.f = ZOOM_SPEED_DEFAULT;
+ o->rest.f.min = ZOOM_SPEED_MIN;
+ o->rest.f.max = ZOOM_SPEED_MAX;
+ o->rest.f.precision = ZOOM_SPEED_PRECISION;
+
+ o = &zs->opt[ZOOM_SCREEN_OPTION_TIMESTEP];
+ o->name = "timestep";
+ o->shortDesc = "Timestep";
+ o->longDesc = "Zoom Timestep";
+ o->type = CompOptionTypeFloat;
+ o->value.f = ZOOM_TIMESTEP_DEFAULT;
+ o->rest.f.min = ZOOM_TIMESTEP_MIN;
+ o->rest.f.max = ZOOM_TIMESTEP_MAX;
+ o->rest.f.precision = ZOOM_TIMESTEP_PRECISION;
+}
+
+#define MIN_Z 0.001f
+
+static int
+adjustZoomVelocity (ZoomScreen *zs)
+{
+ float d, adjust, amount;
+
+ d = (zs->newZoom - zs->currentZoom) * 200.0f;
+
+ adjust = d * 0.002f;
+ amount = fabs (d);
+ if (amount < 1.0f)
+ amount = 1.0f;
+ else if (amount > 10.0f)
+ amount = 10.0f;
+
+ zs->zVelocity = (amount * zs->zVelocity + adjust) / (amount + 1.0f);
+
+ return (fabs (d) < 0.1f && fabs (zs->zVelocity) < 0.005f);
+}
+
+static void
+zoomPreparePaintScreen (CompScreen *s,
+ int msSinceLastPaint)
+{
+ ZOOM_SCREEN (s);
+
+ if (zs->grabIndex)
+ {
+ int steps;
+ float amount, chunk;
+
+ amount = msSinceLastPaint * 0.05f * zs->speed;
+ steps = amount / (0.5f * zs->timestep);
+ if (!steps) steps = 1;
+ chunk = amount / (float) steps;
+
+ while (steps--)
+ {
+ zs->xVelocity /= 1.25f;
+ zs->yVelocity /= 1.25f;
+
+ if (fabs (zs->xVelocity) < 0.001f)
+ zs->xVelocity = 0.0f;
+ if (fabs (zs->yVelocity) < 0.001f)
+ zs->yVelocity = 0.0f;
+
+ zs->xTranslate += zs->xVelocity * chunk;
+ if (zs->xTranslate < -zs->maxTranslate)
+ {
+ zs->xTranslate = -zs->maxTranslate;
+ zs->xVelocity = 0.0f;
+ }
+ else if (zs->xTranslate > zs->maxTranslate)
+ {
+ zs->xTranslate = zs->maxTranslate;
+ zs->xVelocity = 0.0f;
+ }
+
+ zs->yTranslate += zs->yVelocity * chunk;
+ if (zs->yTranslate < -zs->maxTranslate)
+ {
+ zs->yTranslate = -zs->maxTranslate;
+ zs->yVelocity = 0.0f;
+ }
+ else if (zs->yTranslate > zs->maxTranslate)
+ {
+ zs->yTranslate = zs->maxTranslate;
+ zs->yVelocity = 0.0f;
+ }
+
+ if (adjustZoomVelocity (zs))
+ {
+ zs->currentZoom = zs->newZoom;
+ zs->zVelocity = 0.0f;
+ }
+ else
+ {
+ zs->currentZoom += (zs->zVelocity * msSinceLastPaint) /
+ s->redrawTime;
+ }
+
+ zs->ztrans = DEFAULT_Z_CAMERA * zs->currentZoom;
+ if (zs->ztrans <= 0.0f)
+ {
+ zs->zVelocity = 0.0f;
+ zs->ztrans = 0.0f;
+ }
+
+ zs->xtrans = -zs->xTranslate * (1.0f - zs->currentZoom);
+ zs->ytrans = zs->yTranslate * (1.0f - zs->currentZoom);
+
+ if (!zs->grabbed)
+ {
+ if (zs->currentZoom == 1.0f && zs->zVelocity == 0.0f)
+ {
+ zs->xVelocity = zs->yVelocity = 0.0f;
+
+ removeScreenGrab (s, zs->grabIndex, &zs->savedPointer);
+ zs->grabIndex = FALSE;
+ break;
+ }
+ }
+ }
+ }
+
+ UNWRAP (zs, s, preparePaintScreen);
+ (*s->preparePaintScreen) (s, msSinceLastPaint);
+ WRAP (zs, s, preparePaintScreen, zoomPreparePaintScreen);
+}
+
+static void
+zoomDonePaintScreen (CompScreen *s)
+{
+ ZOOM_SCREEN (s);
+
+ if (zs->grabIndex)
+ {
+ if (zs->currentZoom != zs->newZoom ||
+ zs->xVelocity || zs->yVelocity || zs->zVelocity)
+ damageScreen (s);
+ }
+
+ UNWRAP (zs, s, donePaintScreen);
+ (*s->donePaintScreen) (s);
+ WRAP (zs, s, donePaintScreen, zoomDonePaintScreen);
+}
+
+static Bool
+zoomPaintScreen (CompScreen *s,
+ const ScreenPaintAttrib *sAttrib,
+ Region region,
+ unsigned int mask)
+{
+ Bool status;
+
+ ZOOM_SCREEN (s);
+
+ if (zs->grabIndex)
+ {
+ ScreenPaintAttrib sa = *sAttrib;
+
+ sa.xTranslate += zs->xtrans;
+ sa.yTranslate += zs->ytrans;
+
+ sa.zCamera = -zs->ztrans;
+
+ /* hack to get sides rendered correctly */
+ if (zs->xtrans > 0.0f)
+ sa.xRotate += 0.000001f;
+ else
+ sa.xRotate -= 0.000001f;
+
+ mask &= ~PAINT_SCREEN_REGION_MASK;
+ mask |= PAINT_SCREEN_TRANSFORMED_MASK;
+
+ UNWRAP (zs, s, paintScreen);
+ status = (*s->paintScreen) (s, &sa, region, mask);
+ WRAP (zs, s, paintScreen, zoomPaintScreen);
+ }
+ else
+ {
+ UNWRAP (zs, s, paintScreen);
+ status = (*s->paintScreen) (s, sAttrib, region, mask);
+ WRAP (zs, s, paintScreen, zoomPaintScreen);
+ }
+
+ return status;
+}
+
+static void
+zoomIn (CompScreen *s,
+ int x,
+ int y)
+{
+ ZOOM_SCREEN (s);
+
+ /* some other plugin has already grabbed the screen */
+ if (s->maxGrab - zs->grabIndex)
+ return;
+
+ zs->prevPointerX = x;
+ zs->prevPointerY = y;
+
+ if (!zs->grabIndex)
+ {
+ zs->grabIndex = pushScreenGrab (s, s->invisibleCursor);
+
+ zs->savedPointer.x = zs->prevPointerX;
+ zs->savedPointer.y = zs->prevPointerY;
+ }
+
+ if (zs->grabIndex)
+ {
+ zs->grabbed = TRUE;
+
+ zs->newZoom /= 2.0f;
+
+ damageScreen (s);
+
+ if (zs->currentZoom == 1.0f)
+ {
+ zs->xTranslate = (x - s->width / 2) / (float) s->width;
+ zs->yTranslate = (y - s->height / 2) / (float) s->height;
+
+ zs->xTranslate /= zs->newZoom;
+ zs->yTranslate /= zs->newZoom;
+ }
+ }
+}
+
+static void
+zoomOut (CompScreen *s)
+{
+ ZOOM_SCREEN (s);
+
+ if (zs->grabIndex)
+ {
+ zs->newZoom *= 2.0f;
+ if (zs->newZoom > DEFAULT_Z_CAMERA - (DEFAULT_Z_CAMERA / 10.0f))
+ {
+ zs->grabbed = FALSE;
+ zs->newZoom = 1.0f;
+ }
+
+ damageScreen (s);
+ }
+}
+
+static void
+zoomTerminate (CompScreen *s)
+{
+ ZOOM_SCREEN (s);
+
+ if (zs->grabIndex)
+ {
+ zs->newZoom = 1.0f;
+ zs->grabbed = FALSE;
+
+ damageScreen (s);
+ }
+}
+
+static void
+zoomHandleEvent (CompDisplay *d,
+ XEvent *event)
+{
+ CompScreen *s;
+
+ ZOOM_DISPLAY (d);
+
+ switch (event->type) {
+ case KeyPress:
+ case KeyRelease:
+ s = findScreenAtDisplay (d, event->xkey.root);
+ if (s)
+ {
+ ZOOM_SCREEN (s);
+
+ if (EV_KEY (&zs->opt[ZOOM_SCREEN_OPTION_INITIATE], event) ||
+ EV_KEY (&zs->opt[ZOOM_SCREEN_OPTION_IN], event))
+ zoomIn (s,
+ event->xkey.x_root,
+ event->xkey.y_root);
+
+ if (EV_KEY (&zs->opt[ZOOM_SCREEN_OPTION_OUT], event))
+ zoomOut (s);
+
+ if (EV_KEY (&zs->opt[ZOOM_SCREEN_OPTION_TERMINATE], event) ||
+ (event->type == KeyPress &&
+ event->xkey.keycode == s->escapeKeyCode))
+ zoomTerminate (s);
+ }
+ break;
+ case ButtonPress:
+ case ButtonRelease:
+ s = findScreenAtDisplay (d, event->xbutton.root);
+ if (s)
+ {
+ ZOOM_SCREEN (s);
+
+ if (EV_BUTTON (&zs->opt[ZOOM_SCREEN_OPTION_INITIATE], event) ||
+ EV_BUTTON (&zs->opt[ZOOM_SCREEN_OPTION_IN], event))
+ zoomIn (s,
+ event->xbutton.x_root,
+ event->xbutton.y_root);
+
+ if (EV_BUTTON (&zs->opt[ZOOM_SCREEN_OPTION_OUT], event))
+ zoomOut (s);
+
+ if (EV_BUTTON (&zs->opt[ZOOM_SCREEN_OPTION_TERMINATE], event))
+ zoomTerminate (s);
+ }
+ break;
+ case MotionNotify:
+ s = findScreenAtDisplay (d, event->xmotion.root);
+ if (s)
+ {
+ ZOOM_SCREEN (s);
+
+ if (zs->grabIndex && zs->grabbed)
+ {
+ GLfloat pointerDx;
+ GLfloat pointerDy;
+
+ pointerDx = event->xmotion.x_root - zs->prevPointerX;
+ pointerDy = event->xmotion.y_root - zs->prevPointerY;
+ zs->prevPointerX = event->xmotion.x_root;
+ zs->prevPointerY = event->xmotion.y_root;
+
+ 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)
+ {
+ zs->prevPointerX = s->width / 2;
+ zs->prevPointerY = s->height / 2;
+
+ XWarpPointer (d->display, None, s->root, 0, 0, 0, 0,
+ zs->prevPointerX, zs->prevPointerY);
+ }
+
+ if (zs->pointerInvertY)
+ pointerDy = -pointerDy;
+
+ zs->xVelocity += pointerDx * zs->pointerSensitivity;
+ zs->yVelocity += pointerDy * zs->pointerSensitivity;
+
+ damageScreen (s);
+ }
+ }
+ default:
+ break;
+ }
+
+ UNWRAP (zd, d, handleEvent);
+ (*d->handleEvent) (d, event);
+ WRAP (zd, d, handleEvent, zoomHandleEvent);
+}
+
+static void
+zoomUpdateCubeOptions (CompScreen *s)
+{
+ CompPlugin *p;
+
+ ZOOM_SCREEN (s);
+
+ p = findActivePlugin ("cube");
+ if (p && p->vTable->getScreenOptions)
+ {
+ CompOption *options, *option;
+ int nOptions;
+
+ options = (*p->vTable->getScreenOptions) (s, &nOptions);
+ option = compFindOption (options, nOptions, "in", 0);
+ if (option)
+ zs->maxTranslate = option->value.b ? 0.85f : 1.5f;
+ }
+}
+
+static Bool
+zoomSetScreenOptionForPlugin (CompScreen *s,
+ char *plugin,
+ char *name,
+ CompOptionValue *value)
+{
+ Bool status;
+
+ ZOOM_SCREEN (s);
+
+ UNWRAP (zs, s, setScreenOptionForPlugin);
+ status = (*s->setScreenOptionForPlugin) (s, plugin, name, value);
+ WRAP (zs, s, setScreenOptionForPlugin, zoomSetScreenOptionForPlugin);
+
+ if (status && strcmp (plugin, "cube") == 0)
+ zoomUpdateCubeOptions (s);
+
+ return status;
+}
+
+static Bool
+zoomInitDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ ZoomDisplay *zd;
+
+ zd = malloc (sizeof (ZoomDisplay));
+ if (!zd)
+ return FALSE;
+
+ zd->screenPrivateIndex = allocateScreenPrivateIndex (d);
+ if (zd->screenPrivateIndex < 0)
+ {
+ free (zd);
+ return FALSE;
+ }
+
+ WRAP (zd, d, handleEvent, zoomHandleEvent);
+
+ d->privates[displayPrivateIndex].ptr = zd;
+
+ return TRUE;
+}
+
+static void
+zoomFiniDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ ZOOM_DISPLAY (d);
+
+ freeScreenPrivateIndex (d, zd->screenPrivateIndex);
+
+ UNWRAP (zd, d, handleEvent);
+
+ free (zd);
+}
+
+static Bool
+zoomInitScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ ZoomScreen *zs;
+
+ ZOOM_DISPLAY (s->display);
+
+ zs = malloc (sizeof (ZoomScreen));
+ if (!zs)
+ return FALSE;
+
+ zs->grabIndex = 0;
+
+ zs->currentZoom = 1.0f;
+ zs->newZoom = 1.0f;
+
+ zs->xVelocity = 0.0f;
+ zs->yVelocity = 0.0f;
+ zs->zVelocity = 0.0f;
+
+ zs->xTranslate = 0.0f;
+ zs->yTranslate = 0.0f;
+
+ zs->maxTranslate = 0.0f;
+
+ zs->savedPointer.x = 0;
+ zs->savedPointer.y = 0;
+ zs->prevPointerX = 0;
+ zs->prevPointerY = 0;
+
+ zs->grabbed = FALSE;
+
+ zs->pointerInvertY = ZOOM_POINTER_INVERT_Y_DEFAULT;
+ zs->pointerSensitivity = ZOOM_POINTER_SENSITIVITY_DEFAULT *
+ ZOOM_POINTER_SENSITIVITY_FACTOR;
+
+ zs->speed = ZOOM_SPEED_DEFAULT;
+ zs->timestep = ZOOM_TIMESTEP_DEFAULT;
+
+ zoomScreenInitOptions (zs, s->display->display);
+
+ addScreenBinding (s, &zs->opt[ZOOM_SCREEN_OPTION_INITIATE].value.bind);
+ addScreenBinding (s, &zs->opt[ZOOM_SCREEN_OPTION_IN].value.bind);
+
+ WRAP (zs, s, preparePaintScreen, zoomPreparePaintScreen);
+ WRAP (zs, s, donePaintScreen, zoomDonePaintScreen);
+ WRAP (zs, s, paintScreen, zoomPaintScreen);
+ WRAP (zs, s, setScreenOptionForPlugin, zoomSetScreenOptionForPlugin);
+
+ s->privates[zd->screenPrivateIndex].ptr = zs;
+
+ zoomUpdateCubeOptions (s);
+
+ return TRUE;
+}
+
+static void
+zoomFiniScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ ZOOM_SCREEN (s);
+
+ UNWRAP (zs, s, preparePaintScreen);
+ UNWRAP (zs, s, donePaintScreen);
+ UNWRAP (zs, s, paintScreen);
+ UNWRAP (zs, s, setScreenOptionForPlugin);
+
+ free (zs);
+}
+
+static Bool
+zoomInit (CompPlugin *p)
+{
+ displayPrivateIndex = allocateDisplayPrivateIndex ();
+ if (displayPrivateIndex < 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+zoomFini (CompPlugin *p)
+{
+ if (displayPrivateIndex >= 0)
+ freeDisplayPrivateIndex (displayPrivateIndex);
+}
+
+CompPluginDep zoomDeps[] = {
+ { CompPluginRuleAfter, "cube" }
+};
+
+CompPluginVTable zoomVTable = {
+ "zoom",
+ "Zoom Desktop",
+ "Zoom and pan desktop cube",
+ zoomInit,
+ zoomFini,
+ zoomInitDisplay,
+ zoomFiniDisplay,
+ zoomInitScreen,
+ zoomFiniScreen,
+ 0, /* InitWindow */
+ 0, /* FiniWindow */
+ 0, /* GetDisplayOptions */
+ 0, /* SetDisplayOption */
+ zoomGetScreenOptions,
+ zoomSetScreenOption,
+ zoomDeps,
+ sizeof (zoomDeps) / sizeof (zoomDeps[0])
+};
+
+CompPluginVTable *
+getCompPluginInfo (void)
+{
+ return &zoomVTable;
+}
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 00000000..289e7e93
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,22 @@
+INCLUDES = \
+ @COMPIZ_CFLAGS@ \
+ -I$(top_srcdir)/include \
+ -DPLUGINDIR=\"$(plugindir)\" \
+ -DIMAGEDIR=\"$(imagedir)\"
+
+bin_PROGRAMS = compiz
+
+compiz_LDADD = @COMPIZ_LIBS@ @GL_LIBS@ -lm
+compiz_LDFLAGS = -export-dynamic
+compiz_SOURCES = \
+ main.c \
+ privates.c \
+ texture.c \
+ display.c \
+ screen.c \
+ window.c \
+ event.c \
+ paint.c \
+ option.c \
+ plugin.c \
+ readpng.c
diff --git a/src/action.c b/src/action.c
new file mode 100644
index 00000000..ebcd37e7
--- /dev/null
+++ b/src/action.c
@@ -0,0 +1,41 @@
+/*
+ * 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 <compiz.h>
+
+typedef struct _CompAction {
+ char *name;
+ char *description;
+ CompOptionType type;
+ union {
+ Bool b;
+ int i;
+ float f;
+ } value;
+} CompAction;
diff --git a/src/display.c b/src/display.c
new file mode 100644
index 00000000..57e090cc
--- /dev/null
+++ b/src/display.c
@@ -0,0 +1,1844 @@
+/*
+ * 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 <sys/poll.h>
+#include <unistd.h>
+
+#define XK_MISCELLANY
+#include <X11/keysymdef.h>
+
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/extensions/Xcomposite.h>
+#include <X11/extensions/Xrandr.h>
+/* #include <X11/extensions/Xevie.h> */
+#include <X11/extensions/shape.h>
+
+#include <compiz.h>
+
+static unsigned int virtualModMask[] = {
+ CompAltMask, CompMetaMask, CompSuperMask, CompHyperMask,
+ CompModeSwitchMask, CompNumLockMask, CompScrollLockMask
+};
+
+typedef struct _CompTimeout {
+ struct _CompTimeout *next;
+ int time;
+ int left;
+ CallBackProc callBack;
+ void *closure;
+ CompTimeoutHandle handle;
+} CompTimeout;
+
+static CompTimeout *timeouts = 0;
+static struct timeval lastTimeout;
+static CompTimeoutHandle lastTimeoutHandle = 1;
+
+#define CLICK_TO_FOCUS_DEFAULT TRUE
+
+#define AUTORAISE_DEFAULT TRUE
+
+#define AUTORAISE_DELAY_DEFAULT 1000
+#define AUTORAISE_DELAY_MIN 0
+#define AUTORAISE_DELAY_MAX 10000
+
+#define NUM_OPTIONS(d) (sizeof ((d)->opt) / sizeof (CompOption))
+
+static char *textureFilter[] = { "Fast", "Good", "Best" };
+
+#define NUM_TEXTURE_FILTER (sizeof (textureFilter) / sizeof (textureFilter[0]))
+
+CompDisplay *compDisplays = 0;
+
+static CompDisplay compDisplay;
+
+static char *displayPrivateIndices = 0;
+static int displayPrivateLen = 0;
+
+static int
+reallocDisplayPrivate (int size,
+ void *closure)
+{
+ CompDisplay *d = compDisplays;
+ void *privates;
+
+ if (d)
+ {
+ privates = realloc (d->privates, size * sizeof (CompPrivate));
+ if (!privates)
+ return FALSE;
+
+ d->privates = (CompPrivate *) privates;
+ }
+
+ return TRUE;
+}
+
+int
+allocateDisplayPrivateIndex (void)
+{
+ return allocatePrivateIndex (&displayPrivateLen,
+ &displayPrivateIndices,
+ reallocDisplayPrivate,
+ 0);
+}
+
+void
+freeDisplayPrivateIndex (int index)
+{
+ freePrivateIndex (displayPrivateLen, displayPrivateIndices, index);
+}
+
+static void
+compDisplayInitOptions (CompDisplay *display,
+ char **plugin,
+ int nPlugin)
+{
+ CompOption *o;
+ int i;
+
+ o = &display->opt[COMP_DISPLAY_OPTION_ACTIVE_PLUGINS];
+ o->name = "active_plugins";
+ o->shortDesc = "Active Plugins";
+ o->longDesc = "List of currently active plugins";
+ o->type = CompOptionTypeList;
+ o->value.list.type = CompOptionTypeString;
+ o->value.list.nValue = nPlugin;
+ o->value.list.value = malloc (sizeof (CompOptionValue) * nPlugin);
+ for (i = 0; i < nPlugin; i++)
+ o->value.list.value[i].s = strdup (plugin[i]);
+ o->rest.s.string = 0;
+ o->rest.s.nString = 0;
+
+ display->dirtyPluginList = TRUE;
+
+ o = &display->opt[COMP_DISPLAY_OPTION_TEXTURE_FILTER];
+ o->name = "texture_filter";
+ o->shortDesc = "Texture Filter";
+ o->longDesc = "Texture filtering";
+ o->type = CompOptionTypeString;
+ o->value.s = strdup (defaultTextureFilter);
+ o->rest.s.string = textureFilter;
+ o->rest.s.nString = NUM_TEXTURE_FILTER;
+
+ o = &display->opt[COMP_DISPLAY_OPTION_CLICK_TO_FOCUS];
+ o->name = "click_to_focus";
+ o->shortDesc = "Click To Focus";
+ o->longDesc = "Click on window moves input focus to it";
+ o->type = CompOptionTypeBool;
+ o->value.b = CLICK_TO_FOCUS_DEFAULT;
+
+ o = &display->opt[COMP_DISPLAY_OPTION_AUTORAISE];
+ o->name = "autoraise";
+ o->shortDesc = "Auto-Raise";
+ o->longDesc = "Raise selected windows after interval";
+ o->type = CompOptionTypeBool;
+ o->value.b = AUTORAISE_DEFAULT;
+
+ o = &display->opt[COMP_DISPLAY_OPTION_AUTORAISE_DELAY];
+ o->name = "autoraise_delay";
+ o->shortDesc = "Auto-Raise Delay";
+ o->longDesc = "Interval before raising selected windows";
+ o->type = CompOptionTypeInt;
+ o->value.i = AUTORAISE_DELAY_DEFAULT;
+ o->rest.i.min = AUTORAISE_DELAY_MIN;
+ o->rest.i.max = AUTORAISE_DELAY_MAX;
+}
+
+CompOption *
+compGetDisplayOptions (CompDisplay *display,
+ int *count)
+{
+ *count = NUM_OPTIONS (display);
+ return display->opt;
+}
+
+static Bool
+setDisplayOption (CompDisplay *display,
+ char *name,
+ CompOptionValue *value)
+{
+ CompOption *o;
+ int index;
+
+ o = compFindOption (display->opt, NUM_OPTIONS (display), name, &index);
+ if (!o)
+ return FALSE;
+
+ switch (index) {
+ case COMP_DISPLAY_OPTION_ACTIVE_PLUGINS:
+ if (compSetOptionList (o, value))
+ {
+ display->dirtyPluginList = TRUE;
+ return TRUE;
+ }
+ break;
+ case COMP_DISPLAY_OPTION_TEXTURE_FILTER:
+ if (compSetStringOption (o, value))
+ {
+ CompScreen *s;
+
+ for (s = display->screens; s; s = s->next)
+ damageScreen (s);
+
+ if (strcmp (o->value.s, "Fast") == 0)
+ display->textureFilter = GL_NEAREST;
+ else
+ display->textureFilter = GL_LINEAR;
+
+ return TRUE;
+ }
+ break;
+ case COMP_DISPLAY_OPTION_CLICK_TO_FOCUS:
+ if (compSetBoolOption (o, value))
+ return TRUE;
+ break;
+ case COMP_DISPLAY_OPTION_AUTORAISE:
+ if (compSetBoolOption (o, value))
+ return TRUE;
+ break;
+ case COMP_DISPLAY_OPTION_AUTORAISE_DELAY:
+ if (compSetIntOption (o, value))
+ return TRUE;
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+static Bool
+setDisplayOptionForPlugin (CompDisplay *display,
+ char *plugin,
+ char *name,
+ CompOptionValue *value)
+{
+ CompPlugin *p;
+
+ p = findActivePlugin (plugin);
+ if (p && p->vTable->setDisplayOption)
+ return (*p->vTable->setDisplayOption) (display, name, value);
+
+ return FALSE;
+}
+
+static Bool
+initPluginForDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ return (*p->vTable->initDisplay) (p, d);
+}
+
+static void
+finiPluginForDisplay (CompPlugin *p,
+ CompDisplay *d)
+{
+ (*p->vTable->finiDisplay) (p, d);
+}
+
+static void
+updatePlugins (CompDisplay *d)
+{
+ CompOption *o;
+ CompPlugin *p, **pop = 0;
+ int nPop, i, j;
+
+ d->dirtyPluginList = FALSE;
+
+ o = &d->opt[COMP_DISPLAY_OPTION_ACTIVE_PLUGINS];
+ for (i = 0; i < d->plugin.list.nValue && i < o->value.list.nValue; i++)
+ {
+ if (strcmp (d->plugin.list.value[i].s, o->value.list.value[i].s))
+ break;
+ }
+
+ nPop = d->plugin.list.nValue - i;
+
+ if (nPop)
+ {
+ pop = malloc (sizeof (CompPlugin *) * nPop);
+ if (!pop)
+ {
+ (*d->setDisplayOption) (d, o->name, &d->plugin);
+ return;
+ }
+ }
+
+ for (j = 0; j < nPop; j++)
+ {
+ pop[j] = popPlugin ();
+ d->plugin.list.nValue--;
+ free (d->plugin.list.value[d->plugin.list.nValue].s);
+ }
+
+ for (; i < o->value.list.nValue; i++)
+ {
+ p = 0;
+ for (j = 0; j < nPop; j++)
+ {
+ if (pop[j] && strcmp (pop[j]->vTable->name,
+ o->value.list.value[i].s) == 0)
+ {
+ if (pushPlugin (pop[j]))
+ {
+ p = pop[j];
+ pop[j] = 0;
+ break;
+ }
+ }
+ }
+
+ if (p == 0)
+ {
+ p = loadPlugin (o->value.list.value[i].s);
+ if (p)
+ {
+ if (!pushPlugin (p))
+ {
+ unloadPlugin (p);
+ p = 0;
+ }
+ }
+ }
+
+ if (p)
+ {
+ CompOptionValue *value;
+
+ value = realloc (d->plugin.list.value, sizeof (CompOption) *
+ (d->plugin.list.nValue + 1));
+ if (value)
+ {
+ value[d->plugin.list.nValue].s = strdup (p->vTable->name);
+
+ d->plugin.list.value = value;
+ d->plugin.list.nValue++;
+ }
+ else
+ {
+ p = popPlugin ();
+ unloadPlugin (p);
+ }
+ }
+ }
+
+ for (j = 0; j < nPop; j++)
+ {
+ if (pop[j])
+ unloadPlugin (pop[j]);
+ }
+
+ if (nPop)
+ free (pop);
+
+ (*d->setDisplayOption) (d, o->name, &d->plugin);
+}
+
+static void
+addTimeout (CompTimeout *timeout)
+{
+ CompTimeout *p = 0, *t;
+
+ for (t = timeouts; t; t = t->next)
+ {
+ if (timeout->time < t->left)
+ break;
+
+ p = t;
+ }
+
+ timeout->next = t;
+ timeout->left = timeout->time;
+
+ if (p)
+ p->next = timeout;
+ else
+ timeouts = timeout;
+}
+
+CompTimeoutHandle
+compAddTimeout (int time,
+ CallBackProc callBack,
+ void *closure)
+{
+ CompTimeout *timeout;
+
+ timeout = malloc (sizeof (CompTimeout));
+ if (!timeout)
+ return 0;
+
+ timeout->time = time;
+ timeout->callBack = callBack;
+ timeout->closure = closure;
+ timeout->handle = lastTimeoutHandle++;
+
+ if (lastTimeoutHandle == MAXSHORT)
+ lastTimeoutHandle = 1;
+
+ if (!timeouts)
+ gettimeofday (&lastTimeout, 0);
+
+ addTimeout (timeout);
+
+ return timeout->handle;
+}
+
+void
+compRemoveTimeout (CompTimeoutHandle handle)
+{
+ CompTimeout *p = 0, *t;
+
+ for (t = timeouts; t; t = t->next)
+ {
+ if (t->handle == handle)
+ break;
+
+ p = t;
+ }
+
+ if (t)
+ {
+ if (p)
+ p->next = t->next;
+ else
+ timeouts = t->next;
+
+ free (t);
+ }
+}
+
+#define TIMEVALDIFF(tv1, tv2) \
+ ((tv1)->tv_sec == (tv2)->tv_sec || (tv1)->tv_usec >= (tv2)->tv_usec) ? \
+ ((((tv1)->tv_sec - (tv2)->tv_sec) * 1000000) + \
+ ((tv1)->tv_usec - (tv2)->tv_usec)) / 1000 : \
+ ((((tv1)->tv_sec - 1 - (tv2)->tv_sec) * 1000000) + \
+ (1000000 + (tv1)->tv_usec - (tv2)->tv_usec)) / 1000
+
+static int
+getTimeToNextRedraw (CompScreen *s,
+ struct timeval *lastTv,
+ Bool idle)
+{
+ struct timeval tv;
+ int diff, next;
+ static int timeMult = 1;
+
+ gettimeofday (&tv, 0);
+
+ diff = TIMEVALDIFF (&tv, lastTv);
+
+ if (idle)
+ {
+ if (timeMult > 1)
+ {
+ s->frameStatus = -1;
+ s->redrawTime = s->optimalRedrawTime;
+ timeMult--;
+ }
+ }
+ else
+ {
+ if (diff > s->redrawTime)
+ {
+ if (s->frameStatus > 0)
+ s->frameStatus = 0;
+
+ next = s->optimalRedrawTime * (timeMult + 1);
+ if (diff > next)
+ {
+ s->frameStatus--;
+ if (s->frameStatus < -1)
+ {
+ timeMult++;
+ s->redrawTime = diff = next;
+ }
+ }
+ }
+ else if (diff < s->redrawTime)
+ {
+ if (s->frameStatus < 0)
+ s->frameStatus = 0;
+
+ if (timeMult > 1)
+ {
+ next = s->optimalRedrawTime * (timeMult - 1);
+ if (diff < next)
+ {
+ s->frameStatus++;
+ if (s->frameStatus > 4)
+ {
+ timeMult--;
+ s->redrawTime = next;
+ }
+ }
+ }
+ }
+ }
+
+ if (diff > s->redrawTime)
+ return 0;
+
+ return s->redrawTime - diff;
+}
+
+static CompWindow *
+findWindowAt (CompDisplay *d,
+ Window root,
+ int x,
+ int y)
+{
+ CompScreen *s;
+ CompWindow *w;
+
+ for (s = d->screens; s; s = s->next)
+ {
+ if (s->root == root && s->maxGrab == 0)
+ {
+ for (w = s->reverseWindows; w; w = w->prev)
+ {
+ if (x >= w->attrib.x &&
+ y >= w->attrib.y &&
+ x < w->attrib.x + w->width &&
+ y < w->attrib.y + w->height)
+ return w;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static Window
+translateToRootWindow (CompDisplay *d,
+ Window child)
+{
+ CompScreen *s;
+
+ for (s = d->screens; s; s = s->next)
+ {
+ if (s->root == child || s->grabWindow == child)
+ return s->root;
+ }
+
+ return child;
+}
+
+void
+updateModifierMappings (CompDisplay *d)
+{
+ XModifierKeymap *modmap;
+ unsigned int modMask[CompModNum];
+ int i, minKeycode, maxKeycode, keysymsPerKeycode = 0;
+
+ for (i = 0; i < CompModNum; i++)
+ modMask[i] = 0;
+
+ XDisplayKeycodes (d->display, &minKeycode, &maxKeycode);
+ XGetKeyboardMapping (d->display, minKeycode, (maxKeycode - minKeycode + 1),
+ &keysymsPerKeycode);
+
+ modmap = XGetModifierMapping (d->display);
+ if (modmap && modmap->max_keypermod > 0)
+ {
+ static int maskTable[] = {
+ ShiftMask, LockMask, ControlMask, Mod1Mask,
+ Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask
+ };
+ KeySym keysym;
+ int index, size, mask;
+
+ size = (sizeof (maskTable) / sizeof (int)) * modmap->max_keypermod;
+
+ for (i = 0; i < size; i++)
+ {
+ if (!modmap->modifiermap[i])
+ continue;
+
+ index = 0;
+ do
+ {
+ keysym = XKeycodeToKeysym (d->display,
+ modmap->modifiermap[i],
+ index++);
+ } while (!keysym && index < keysymsPerKeycode);
+
+ if (keysym)
+ {
+ mask = maskTable[i / modmap->max_keypermod];
+
+ if (keysym == XK_Alt_L ||
+ keysym == XK_Alt_R)
+ {
+ modMask[CompModAlt] |= mask;
+ }
+ else if (keysym == XK_Meta_L ||
+ keysym == XK_Meta_R)
+ {
+ modMask[CompModMeta] |= mask;
+ }
+ else if (keysym == XK_Super_L ||
+ keysym == XK_Super_R)
+ {
+ modMask[CompModSuper] |= mask;
+ }
+ else if (keysym == XK_Hyper_L ||
+ keysym == XK_Hyper_R)
+ {
+ modMask[CompModHyper] |= mask;
+ }
+ else if (keysym == XK_Mode_switch)
+ {
+ modMask[CompModModeSwitch] |= mask;
+ }
+ else if (keysym == XK_Scroll_Lock)
+ {
+ modMask[CompModScrollLock] |= mask;
+ }
+ else if (keysym == XK_Num_Lock)
+ {
+ modMask[CompModNumLock] |= mask;
+ }
+ }
+ }
+
+ if (modmap)
+ XFreeModifiermap (modmap);
+
+ for (i = 0; i < CompModNum; i++)
+ {
+ if (!modMask[i])
+ modMask[i] = CompNoMask;
+ }
+
+ if (memcmp (modMask, d->modMask, sizeof (modMask)))
+ {
+ CompScreen *s;
+
+ memcpy (d->modMask, modMask, sizeof (modMask));
+
+ d->ignoredModMask = LockMask |
+ (modMask[CompModNumLock] & ~CompNoMask) |
+ (modMask[CompModScrollLock] & ~CompNoMask);
+
+ for (s = d->screens; s; s = s->next)
+ updatePassiveGrabs (s);
+ }
+ }
+}
+
+unsigned int
+virtualToRealModMask (CompDisplay *d,
+ unsigned int modMask)
+{
+ int i;
+
+ for (i = 0; i < CompModNum; i++)
+ {
+ if (modMask & virtualModMask[i])
+ {
+ modMask &= ~virtualModMask[i];
+ modMask |= d->modMask[i];
+ }
+ }
+
+ return (modMask & ~(CompPressMask | CompReleaseMask));
+}
+
+static unsigned int
+realToVirtualModMask (CompDisplay *d,
+ unsigned int modMask)
+{
+ int i;
+
+ for (i = 0; i < CompModNum; i++)
+ {
+ if (modMask & d->modMask[i])
+ modMask |= virtualModMask[i];
+ }
+
+ return modMask;
+}
+
+void
+eventLoop (void)
+{
+ XEvent event;
+ struct pollfd ufd;
+ int timeDiff;
+ struct timeval tv;
+ Region tmpRegion;
+ CompDisplay *display = compDisplays;
+ CompScreen *s = display->screens;
+ int timeToNextRedraw = 0;
+ CompWindow *move = 0;
+ int px = 0, py = 0;
+ int moveX = 0, moveY = 0;
+ CompTimeout *t;
+ Bool idle = TRUE;
+
+ tmpRegion = XCreateRegion ();
+ if (!tmpRegion)
+ {
+ fprintf (stderr, "%s: Couldn't create region\n", programName);
+ return;
+ }
+
+ ufd.fd = ConnectionNumber (display->display);
+ ufd.events = POLLIN;
+
+ for (;;)
+ {
+ if (display->dirtyPluginList)
+ updatePlugins (display);
+
+ if (restartSignal)
+ {
+ execvp (programName, programArgv);
+ exit (1);
+ }
+
+ while (XPending (display->display))
+ {
+ XNextEvent (display->display, &event);
+
+ /* translate root window coordinates */
+ if (testMode)
+ {
+ Window root, child;
+
+ switch (event.type) {
+ case ButtonPress:
+ if (!move)
+ {
+ px = event.xbutton.x;
+ py = event.xbutton.y;
+
+ move = findWindowAt (display, event.xbutton.window,
+ px, py);
+ if (move)
+ {
+ moveX = move->attrib.x;
+ moveY = move->attrib.y;
+ XRaiseWindow (display->display, move->id);
+ continue;
+ }
+ }
+ /* fall-through */
+ case ButtonRelease:
+ move = 0;
+
+ root = translateToRootWindow (display,
+ event.xbutton.window);
+ XTranslateCoordinates (display->display,
+ event.xbutton.root, root,
+ event.xbutton.x_root,
+ event.xbutton.y_root,
+ &event.xbutton.x_root,
+ &event.xbutton.y_root,
+ &child);
+ event.xbutton.root = root;
+ break;
+ case KeyPress:
+ case KeyRelease:
+ root = translateToRootWindow (display, event.xkey.window);
+ XTranslateCoordinates (display->display,
+ event.xkey.root, root,
+ event.xkey.x_root,
+ event.xkey.y_root,
+ &event.xkey.x_root,
+ &event.xkey.y_root,
+ &child);
+ event.xkey.root = root;
+ break;
+ case MotionNotify:
+ if (move)
+ {
+ moveX += event.xbutton.x - px;
+ moveY += event.xbutton.y - py;
+ px = event.xbutton.x;
+ py = event.xbutton.y;
+
+ XMoveWindow (display->display, move->id, moveX, moveY);
+
+ continue;
+ }
+
+ root = translateToRootWindow (display,
+ event.xmotion.window);
+ XTranslateCoordinates (display->display,
+ event.xmotion.root, root,
+ event.xmotion.x_root,
+ event.xmotion.y_root,
+ &event.xmotion.x_root,
+ &event.xmotion.y_root,
+ &child);
+ event.xmotion.root = root;
+ default:
+ break;
+ }
+ }
+
+ /* add virtual modifiers */
+ switch (event.type) {
+ case ButtonPress:
+ event.xbutton.state |= CompPressMask;
+ event.xbutton.state =
+ realToVirtualModMask (display, event.xbutton.state);
+ break;
+ case ButtonRelease:
+ event.xbutton.state |= CompReleaseMask;
+ event.xbutton.state =
+ realToVirtualModMask (display, event.xbutton.state);
+ break;
+ case KeyPress:
+ event.xkey.state |= CompPressMask;
+ event.xkey.state = realToVirtualModMask (display,
+ event.xkey.state);
+ break;
+ case KeyRelease:
+ event.xkey.state |= CompReleaseMask;
+ event.xkey.state = realToVirtualModMask (display,
+ event.xkey.state);
+ break;
+ case MotionNotify:
+ event.xmotion.state =
+ realToVirtualModMask (display, event.xmotion.state);
+ break;
+ default:
+ break;
+ }
+
+ sn_display_process_event (display->snDisplay, &event);
+
+ (*display->handleEvent) (display, &event);
+ }
+
+ if (s->damageMask)
+ {
+ /* sync with server */
+ glFinish ();
+
+ timeToNextRedraw = getTimeToNextRedraw (s, &s->lastRedraw, idle);
+ if (timeToNextRedraw)
+ timeToNextRedraw = poll (&ufd, 1, timeToNextRedraw);
+
+ if (timeToNextRedraw == 0)
+ {
+ gettimeofday (&tv, 0);
+
+ timeDiff = TIMEVALDIFF (&tv, &s->lastRedraw);
+
+ s->stencilRef = 0;
+
+ (*s->preparePaintScreen) (s, idle ? s->redrawTime : timeDiff);
+
+ if (s->damageMask & COMP_SCREEN_DAMAGE_REGION_MASK)
+ {
+ XIntersectRegion (s->damage, &s->region, tmpRegion);
+
+ if (tmpRegion->numRects == 1 &&
+ tmpRegion->rects->x1 == 0 &&
+ tmpRegion->rects->y1 == 0 &&
+ tmpRegion->rects->x2 == s->width &&
+ tmpRegion->rects->y2 == s->height)
+ damageScreen (s);
+ }
+
+ EMPTY_REGION (s->damage);
+
+ if (s->damageMask & COMP_SCREEN_DAMAGE_ALL_MASK)
+ {
+ s->damageMask = 0;
+
+ (*s->paintScreen) (s,
+ &defaultScreenPaintAttrib,
+ &s->region,
+ PAINT_SCREEN_REGION_MASK |
+ PAINT_SCREEN_FULL_MASK);
+
+ glXSwapBuffers (s->display->display, s->root);
+ }
+ else if (s->damageMask & COMP_SCREEN_DAMAGE_REGION_MASK)
+ {
+ s->damageMask = 0;
+
+ if ((*s->paintScreen) (s,
+ &defaultScreenPaintAttrib,
+ tmpRegion,
+ PAINT_SCREEN_REGION_MASK))
+ {
+ BoxPtr pBox;
+ int nBox, y;
+
+ /*
+ pBox = tmpRegion->rects;
+ nBox = tmpRegion->numRects;
+ while (nBox--)
+ {
+ y = s->height - pBox->y2;
+
+ glXCopySubBufferMESA (s->display->display,
+ s->root,
+ pBox->x1, y,
+ pBox->x2 - pBox->x1,
+ pBox->y2 - pBox->y1);
+
+ pBox++;
+ }
+
+ */ /* ugly empty rect flush hack */ /*
+ glXCopySubBufferMESA (s->display->display, s->root,
+ 0, 0, 0, 0);
+ */
+
+ glEnable (GL_SCISSOR_TEST);
+ glDrawBuffer (GL_FRONT);
+
+ pBox = tmpRegion->rects;
+ nBox = tmpRegion->numRects;
+ while (nBox--)
+ {
+ y = s->height - pBox->y2;
+
+ glBitmap (0, 0, 0, 0,
+ pBox->x1 - s->rasterX, y - s->rasterY,
+ NULL);
+
+ s->rasterX = pBox->x1;
+ s->rasterY = y;
+
+ glScissor (pBox->x1, y,
+ pBox->x2 - pBox->x1,
+ pBox->y2 - pBox->y1);
+
+ glCopyPixels (pBox->x1, y,
+ pBox->x2 - pBox->x1,
+ pBox->y2 - pBox->y1,
+ GL_COLOR);
+
+ pBox++;
+ }
+
+ glDrawBuffer (GL_BACK);
+ glDisable (GL_SCISSOR_TEST);
+ glFlush ();
+ }
+ else
+ {
+ (*s->paintScreen) (s,
+ &defaultScreenPaintAttrib,
+ &s->region,
+ PAINT_SCREEN_FULL_MASK);
+
+ glXSwapBuffers (s->display->display, s->root);
+ }
+ }
+
+ s->lastRedraw = tv;
+
+ (*s->donePaintScreen) (s);
+
+ /* remove destroyed windows */
+ while (s->pendingDestroys)
+ {
+ CompWindow *w;
+
+ for (w = s->windows; w; w = w->next)
+ {
+ if (w->destroyed)
+ {
+ addWindowDamage (w);
+ removeWindow (w);
+ break;
+ }
+ }
+
+ s->pendingDestroys--;
+ }
+ }
+
+ idle = FALSE;
+ }
+ else
+ {
+ if (timeouts)
+ {
+ if (timeouts->left > 0)
+ poll (&ufd, 1, timeouts->left);
+
+ gettimeofday (&tv, 0);
+
+ timeDiff = TIMEVALDIFF (&tv, &lastTimeout);
+
+ for (t = timeouts; t; t = t->next)
+ t->left -= timeDiff;
+
+ while (timeouts && timeouts->left <= 0)
+ {
+ t = timeouts;
+ if ((*t->callBack) (t->closure))
+ {
+ timeouts = t->next;
+ addTimeout (t);
+ }
+ else
+ {
+ timeouts = t->next;
+ free (t);
+ }
+ }
+
+ lastTimeout = tv;
+ }
+ else
+ {
+ poll (&ufd, 1, 1000);
+ }
+
+ idle = TRUE;
+ }
+ }
+}
+
+static int errors = 0;
+
+static int
+errorHandler (Display *dpy,
+ XErrorEvent *e)
+{
+
+#ifdef DEBUG
+ char str[128];
+ char *name = 0;
+ int o;
+#endif
+
+ errors++;
+
+#ifdef DEBUG
+ XGetErrorDatabaseText (dpy, "XlibMessage", "XError", "", str, 128);
+ fprintf (stderr, "%s", str);
+
+ o = e->error_code - compDisplays->damageError;
+ switch (o) {
+ case BadDamage:
+ name = "BadDamage";
+ break;
+ default:
+ break;
+ }
+
+ if (name)
+ {
+ fprintf (stderr, ": %s\n ", name);
+ }
+ else
+ {
+ XGetErrorText (dpy, e->error_code, str, 128);
+ fprintf (stderr, ": %s\n ", str);
+ }
+
+ XGetErrorDatabaseText (dpy, "XlibMessage", "MajorCode", "%d", str, 128);
+ fprintf (stderr, str, e->request_code);
+
+ sprintf (str, "%d", e->request_code);
+ XGetErrorDatabaseText (dpy, "XRequest", str, "", str, 128);
+ if (strcmp (str, ""))
+ fprintf (stderr, " (%s)", str);
+ fprintf (stderr, "\n ");
+
+ XGetErrorDatabaseText (dpy, "XlibMessage", "MinorCode", "%d", str, 128);
+ fprintf (stderr, str, e->minor_code);
+ fprintf (stderr, "\n ");
+
+ XGetErrorDatabaseText (dpy, "XlibMessage", "ResourceID", "%d", str, 128);
+ fprintf (stderr, str, e->resourceid);
+ fprintf (stderr, "\n");
+
+ /* abort (); */
+#endif
+
+ return 0;
+}
+
+int
+compCheckForError (Display *dpy)
+{
+ int e;
+
+ XSync (dpy, FALSE);
+
+ e = errors;
+ errors = 0;
+
+ return e;
+}
+
+#define PING_DELAY 5000
+
+static Bool
+pingTimeout (void *closure)
+{
+ CompDisplay *d = closure;
+ CompScreen *s;
+ CompWindow *w;
+ XEvent ev;
+ int ping = d->lastPing + 1;
+
+ ev.type = ClientMessage;
+ ev.xclient.window = 0;
+ ev.xclient.message_type = d->wmProtocolsAtom;
+ ev.xclient.format = 32;
+ ev.xclient.data.l[0] = d->wmPingAtom;
+ ev.xclient.data.l[1] = ping;
+ ev.xclient.data.l[2] = 0;
+ ev.xclient.data.l[3] = 0;
+ ev.xclient.data.l[4] = 0;
+
+ for (s = d->screens; s; s = s->next)
+ {
+ for (w = s->windows; w; w = w->next)
+ {
+ if (w->attrib.map_state != IsViewable)
+ continue;
+
+ if (!(w->type & CompWindowTypeNormalMask))
+ continue;
+
+ if (w->protocols & CompWindowProtocolPingMask)
+ {
+ if (w->transientFor)
+ continue;
+
+ if (w->lastPong < d->lastPing)
+ {
+ if (w->alive)
+ {
+ w->alive = FALSE;
+ w->paint.saturation = 0;
+
+ addWindowDamage (w);
+ }
+ }
+
+ ev.xclient.window = w->id;
+ ev.xclient.data.l[2] = w->id;
+
+ XSendEvent (d->display, w->id, FALSE, NoEventMask, &ev);
+ }
+ }
+ }
+
+ d->lastPing = ping;
+
+ return TRUE;
+}
+
+Bool
+addDisplay (char *name,
+ char **plugin,
+ int nPlugin)
+{
+ CompDisplay *d;
+ Display *dpy;
+ Window focus;
+ int revertTo, i;
+
+ d = &compDisplay;
+
+ if (displayPrivateLen)
+ {
+ d->privates = malloc (displayPrivateLen * sizeof (CompPrivate));
+ if (!d->privates)
+ return FALSE;
+ }
+ else
+ d->privates = 0;
+
+ d->screenPrivateIndices = 0;
+ d->screenPrivateLen = 0;
+
+ for (i = 0; i < CompModNum; i++)
+ d->modMask[i] = CompNoMask;
+
+ d->ignoredModMask = LockMask;
+
+ d->plugin.list.type = CompOptionTypeString;
+ d->plugin.list.nValue = 0;
+ d->plugin.list.value = 0;
+
+ compDisplayInitOptions (d, plugin, nPlugin);
+
+ d->textureFilter = GL_LINEAR;
+ d->below = None;
+
+ d->activeWindow = 0;
+
+ d->autoRaiseHandle = 0;
+ d->autoRaiseWindow = None;
+
+ d->display = dpy = XOpenDisplay (name);
+ if (!d->display)
+ {
+ fprintf (stderr, "%s: Couldn't open display %s\n",
+ programName, XDisplayName (name));
+ return FALSE;
+ }
+
+ snprintf (d->displayString, 255, "DISPLAY=%s", DisplayString (dpy));
+
+#ifdef DEBUG
+ XSynchronize (dpy, TRUE);
+#endif
+
+ XSetErrorHandler (errorHandler);
+
+ updateModifierMappings (d);
+
+ d->setDisplayOption = setDisplayOption;
+ d->setDisplayOptionForPlugin = setDisplayOptionForPlugin;
+
+ d->initPluginForDisplay = initPluginForDisplay;
+ d->finiPluginForDisplay = finiPluginForDisplay;
+
+ d->handleEvent = handleEvent;
+
+ d->supportedAtom = XInternAtom (dpy, "_NET_SUPPORTED", 0);
+ d->supportingWmCheckAtom = XInternAtom (dpy, "_NET_SUPPORTING_WM_CHECK", 0);
+
+ d->utf8StringAtom = XInternAtom (dpy, "UTF8_STRING", 0);
+
+ d->wmNameAtom = XInternAtom (dpy, "_NET_WM_NAME", 0);
+
+ d->winTypeAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE", 0);
+ d->winTypeDesktopAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_DESKTOP",
+ 0);
+ d->winTypeDockAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_DOCK", 0);
+ d->winTypeToolbarAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_TOOLBAR",
+ 0);
+ d->winTypeMenuAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_MENU", 0);
+ d->winTypeUtilAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_UTILITY",
+ 0);
+ d->winTypeSplashAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_SPLASH", 0);
+ d->winTypeDialogAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_DIALOG", 0);
+ d->winTypeNormalAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_NORMAL", 0);
+
+ d->winOpacityAtom = XInternAtom (dpy, "_NET_WM_WINDOW_OPACITY", 0);
+ d->winBrightnessAtom = XInternAtom (dpy, "_NET_WM_WINDOW_BRIGHTNESS", 0);
+ d->winSaturationAtom = XInternAtom (dpy, "_NET_WM_WINDOW_SATURATION", 0);
+
+ d->winActiveAtom = XInternAtom (dpy, "_NET_ACTIVE_WINDOW", 0);
+
+ d->workareaAtom = XInternAtom (dpy, "_NET_WORKAREA", 0);
+
+ d->desktopViewportAtom = XInternAtom (dpy, "_NET_DESKTOP_VIEWPORT", 0);
+ d->desktopGeometryAtom = XInternAtom (dpy, "_NET_DESKTOP_GEOMETRY", 0);
+ d->currentDesktopAtom = XInternAtom (dpy, "_NET_CURRENT_DESKTOP", 0);
+ d->numberOfDesktopsAtom = XInternAtom (dpy, "_NET_NUMBER_OF_DESKTOPS", 0);
+
+ d->winStateAtom = XInternAtom (dpy, "_NET_WM_STATE", 0);
+ d->winStateModalAtom =
+ XInternAtom (dpy, "_NET_WM_STATE_MODAL", 0);
+ d->winStateStickyAtom =
+ XInternAtom (dpy, "_NET_WM_STATE_STICKY", 0);
+ d->winStateMaximizedVertAtom =
+ XInternAtom (dpy, "_NET_WM_STATE_MAXIMIZED_VERT", 0);
+ d->winStateMaximizedHorzAtom =
+ XInternAtom (dpy, "_NET_WM_STATE_MAXIMIZED_HORZ", 0);
+ d->winStateShadedAtom =
+ XInternAtom (dpy, "_NET_WM_STATE_SHADED", 0);
+ d->winStateSkipTaskbarAtom =
+ XInternAtom (dpy, "_NET_WM_STATE_SKIP_TASKBAR", 0);
+ d->winStateSkipPagerAtom =
+ XInternAtom (dpy, "_NET_WM_STATE_SKIP_PAGER", 0);
+ d->winStateHiddenAtom =
+ XInternAtom (dpy, "_NET_WM_STATE_HIDDEN", 0);
+ d->winStateFullscreenAtom =
+ XInternAtom (dpy, "_NET_WM_STATE_FULLSCREEN", 0);
+ d->winStateAboveAtom =
+ XInternAtom (dpy, "_NET_WM_STATE_ABOVE", 0);
+ d->winStateBelowAtom =
+ XInternAtom (dpy, "_NET_WM_STATE_BELOW", 0);
+ d->winStateDemandsAttentionAtom =
+ XInternAtom (dpy, "_NET_WM_STATE_DEMANDS_ATTENTION", 0);
+ d->winStateDisplayModalAtom =
+ XInternAtom (dpy, "_NET_WM_STATE_DISPLAY_MODAL", 0);
+
+ d->winActionMoveAtom = XInternAtom (dpy, "_NET_WM_ACTION_MOVE", 0);
+ d->winActionResizeAtom = XInternAtom (dpy, "_NET_WM_ACTION_RESIZE", 0);
+ d->winActionStickAtom = XInternAtom (dpy, "_NET_WM_ACTION_STICK", 0);
+ d->winActionMinimizeAtom =
+ XInternAtom (dpy, "_NET_WM_ACTION_MINIMIZE", 0);
+ d->winActionMaximizeHorzAtom =
+ XInternAtom (dpy, "_NET_WM_ACTION_MAXIMIZE_HORZ", 0);
+ d->winActionMaximizeVertAtom =
+ XInternAtom (dpy, "_NET_WM_ACTION_MAXIMIZE_VERT", 0);
+ d->winActionFullscreenAtom =
+ XInternAtom (dpy, "_NET_WM_ACTION_FULLSCREEN", 0);
+ d->winActionCloseAtom = XInternAtom (dpy, "_NET_WM_ACTION_CLOSE", 0);
+
+ d->wmAllowedActionsAtom = XInternAtom (dpy, "_NET_WM_ALLOWED_ACTIONS", 0);
+
+ d->wmStrutAtom = XInternAtom (dpy, "_NET_WM_STRUT", 0);
+ d->wmStrutPartialAtom = XInternAtom (dpy, "_NET_WM_STRUT_PARTIAL", 0);
+
+ d->clientListAtom = XInternAtom (dpy, "_NET_CLIENT_LIST", 0);
+ d->clientListStackingAtom =
+ XInternAtom (dpy, "_NET_CLIENT_LIST_STACKING", 0);
+
+ d->frameExtentsAtom = XInternAtom (dpy, "_NET_FRAME_EXTENTS", 0);
+ d->frameWindowAtom = XInternAtom (dpy, "_NET_FRAME_WINDOW", 0);
+
+ d->wmStateAtom = XInternAtom (dpy, "WM_STATE", 0);
+ d->wmChangeStateAtom = XInternAtom (dpy, "WM_CHANGE_STATE", 0);
+ d->wmProtocolsAtom = XInternAtom (dpy, "WM_PROTOCOLS", 0);
+ d->wmClientLeaderAtom = XInternAtom (dpy, "WM_CLIENT_LEADER", 0);
+
+ d->wmDeleteWindowAtom = XInternAtom (dpy, "WM_DELETE_WINDOW", 0);
+ d->wmTakeFocusAtom = XInternAtom (dpy, "WM_TAKE_FOCUS", 0);
+ d->wmPingAtom = XInternAtom (dpy, "_NET_WM_PING", 0);
+ d->wmSyncRequestAtom = XInternAtom (dpy, "_NET_WM_SYNC_REQUEST", 0);
+
+ d->wmSyncRequestCounterAtom =
+ XInternAtom (dpy, "_NET_WM_SYNC_REQUEST_COUNTER", 0);
+
+ d->closeWindowAtom = XInternAtom (dpy, "_NET_CLOSE_WINDOW", 0);
+ d->wmMoveResizeAtom = XInternAtom (dpy, "_NET_WM_MOVERESIZE", 0);
+ d->moveResizeWindowAtom = XInternAtom (dpy, "_NET_MOVERESIZE_WINDOW", 0);
+
+ d->showingDesktopAtom = XInternAtom (dpy, "_NET_SHOWING_DESKTOP", 0);
+
+ d->xBackgroundAtom[0] = XInternAtom (dpy, "_XSETROOT_ID", 0);
+ d->xBackgroundAtom[1] = XInternAtom (dpy, "_XROOTPMAP_ID", 0);
+
+ d->panelActionAtom = XInternAtom (dpy, "_GNOME_PANEL_ACTION", 0);
+ d->panelActionMainMenuAtom =
+ XInternAtom (dpy, "_GNOME_PANEL_ACTION_MAIN_MENU", 0);
+ d->panelActionRunDialogAtom =
+ XInternAtom (dpy, "_GNOME_PANEL_ACTION_RUN_DIALOG", 0);
+
+ d->mwmHintsAtom = XInternAtom (dpy, "_MOTIF_WM_HINTS", 0);
+
+ d->managerAtom = XInternAtom (dpy, "MANAGER", 0);
+ d->targetsAtom = XInternAtom (dpy, "TARGETS", 0);
+ d->multipleAtom = XInternAtom (dpy, "MULTIPLE", 0);
+ d->timestampAtom = XInternAtom (dpy, "TIMESTAMP", 0);
+ d->versionAtom = XInternAtom (dpy, "VERSION", 0);
+ d->atomPairAtom = XInternAtom (dpy, "ATOM_PAIR", 0);
+
+ d->snDisplay = sn_display_new (dpy, NULL, NULL);
+ if (!d->snDisplay)
+ return FALSE;
+
+ d->lastPing = 1;
+
+ if (testMode)
+ {
+ d->compositeOpcode = MAXSHORT;
+ d->compositeEvent = MAXSHORT;
+ d->compositeError = MAXSHORT;
+
+ d->damageEvent = MAXSHORT;
+ d->damageError = MAXSHORT;
+ }
+ else
+ {
+ int compositeMajor, compositeMinor;
+
+ if (!XQueryExtension (dpy,
+ COMPOSITE_NAME,
+ &d->compositeOpcode,
+ &d->compositeEvent,
+ &d->compositeError))
+ {
+ fprintf (stderr, "%s: No composite extension\n", programName);
+ return FALSE;
+ }
+
+ XCompositeQueryVersion (dpy, &compositeMajor, &compositeMinor);
+ if (compositeMajor == 0 && compositeMinor < 2)
+ {
+ fprintf (stderr, "%s: Old composite extension\n", programName);
+ return FALSE;
+ }
+
+ if (!XDamageQueryExtension (dpy, &d->damageEvent, &d->damageError))
+ {
+ fprintf (stderr, "%s: No damage extension\n", programName);
+ return FALSE;
+ }
+
+ if (!XRRQueryExtension (dpy, &d->randrEvent, &d->randrError))
+ {
+ fprintf (stderr, "%s: No RandR extension\n", programName);
+ return FALSE;
+ }
+
+ if (!XSyncQueryExtension (dpy, &d->syncEvent, &d->syncError))
+ {
+ fprintf (stderr, "%s: No sync extension\n", programName);
+ return FALSE;
+ }
+ }
+
+ d->shapeExtension = XShapeQueryExtension (dpy,
+ &d->shapeEvent,
+ &d->shapeError);
+
+ compDisplays = d;
+
+ if (testMode)
+ {
+ addScreen (d, 0, None, 0, 0);
+ }
+ else
+ {
+ for (i = 0; i < ScreenCount (dpy); i++)
+ {
+ Window newWmSnOwner = None;
+ Atom wmSnAtom = 0;
+ Time wmSnTimestamp = 0;
+ XEvent event;
+ XSetWindowAttributes attr;
+ Window currentWmSnOwner;
+ char buf[128];
+
+ sprintf (buf, "WM_S%d", i);
+ wmSnAtom = XInternAtom (dpy, buf, 0);
+
+ currentWmSnOwner = XGetSelectionOwner (dpy, wmSnAtom);
+
+ if (currentWmSnOwner != None)
+ {
+ if (!replaceCurrentWm)
+ {
+ fprintf (stderr,
+ "%s: Screen %d on display \"%s\" already "
+ "has a window manager; try using the "
+ "--replace option to replace the current "
+ "window manager.\n",
+ programName, i, DisplayString (dpy));
+
+ continue;
+ }
+
+ XSelectInput (dpy, currentWmSnOwner,
+ StructureNotifyMask);
+ }
+
+ attr.override_redirect = TRUE;
+ attr.event_mask = PropertyChangeMask;
+
+ newWmSnOwner =
+ XCreateWindow (dpy, XRootWindow (dpy, i),
+ -100, -100, 1, 1, 0,
+ CopyFromParent, CopyFromParent,
+ CopyFromParent,
+ CWOverrideRedirect | CWEventMask,
+ &attr);
+
+ XChangeProperty (dpy,
+ newWmSnOwner,
+ d->wmNameAtom,
+ d->utf8StringAtom, 8,
+ PropModeReplace,
+ (unsigned char *) PACKAGE,
+ strlen (PACKAGE));
+
+ XWindowEvent (dpy,
+ newWmSnOwner,
+ PropertyChangeMask,
+ &event);
+
+ wmSnTimestamp = event.xproperty.time;
+
+ XSetSelectionOwner (dpy, wmSnAtom, newWmSnOwner,
+ wmSnTimestamp);
+
+ if (XGetSelectionOwner (dpy, wmSnAtom) != newWmSnOwner)
+ {
+ fprintf (stderr,
+ "%s: Could not acquire window manager "
+ "selection on screen %d display \"%s\"\n",
+ programName, i, DisplayString (dpy));
+
+ XDestroyWindow (dpy, newWmSnOwner);
+
+ continue;
+ }
+
+ /* Send client message indicating that we are now the WM */
+ event.xclient.type = ClientMessage;
+ event.xclient.window = XRootWindow (dpy, i);
+ event.xclient.message_type = d->managerAtom;
+ event.xclient.format = 32;
+ event.xclient.data.l[0] = wmSnTimestamp;
+ event.xclient.data.l[1] = wmSnAtom;
+ event.xclient.data.l[2] = 0;
+ event.xclient.data.l[3] = 0;
+ event.xclient.data.l[4] = 0;
+
+ XSendEvent (dpy, XRootWindow (dpy, i), FALSE,
+ StructureNotifyMask, &event);
+
+ /* Wait for old window manager to go away */
+ if (currentWmSnOwner != None)
+ {
+ do {
+ XWindowEvent (dpy, currentWmSnOwner,
+ StructureNotifyMask, &event);
+ } while (event.type != DestroyNotify);
+ }
+
+ compCheckForError (dpy);
+
+ XCompositeRedirectSubwindows (dpy, XRootWindow (dpy, i),
+ CompositeRedirectManual);
+
+ if (compCheckForError (dpy))
+ {
+ fprintf (stderr, "%s: Another composite manager is already "
+ "running on screen: %d\n", programName, i);
+
+ continue;
+ }
+
+ XSelectInput (dpy, XRootWindow (dpy, i),
+ SubstructureRedirectMask |
+ SubstructureNotifyMask |
+ StructureNotifyMask |
+ PropertyChangeMask |
+ LeaveWindowMask |
+ EnterWindowMask |
+ KeyPressMask |
+ KeyReleaseMask |
+ FocusChangeMask |
+ ExposureMask);
+
+ if (compCheckForError (dpy))
+ {
+ fprintf (stderr, "%s: Another window manager is "
+ "already running on screen: %d\n",
+ programName, i);
+
+ continue;
+ }
+
+ if (!addScreen (d, i, newWmSnOwner, wmSnAtom, wmSnTimestamp))
+ {
+ fprintf (stderr, "%s: Failed to manage screen: %d\n",
+ programName, i);
+ }
+ }
+ }
+
+ if (!d->screens)
+ {
+ fprintf (stderr, "%s: No managable screens found on display %s\n",
+ programName, XDisplayName (name));
+ return FALSE;
+ }
+
+ XGetInputFocus (dpy, &focus, &revertTo);
+
+ if (focus == None || focus == PointerRoot)
+ {
+ focusDefaultWindow (d);
+ }
+ else
+ {
+ CompWindow *w;
+
+ w = findWindowAtDisplay (d, focus);
+ if (w)
+ {
+ moveInputFocusToWindow (w);
+ }
+ else
+ focusDefaultWindow (d);
+ }
+
+ d->pingHandle = compAddTimeout (PING_DELAY, pingTimeout, d);
+
+ return TRUE;
+}
+
+void
+focusDefaultWindow (CompDisplay *d)
+{
+ CompScreen *s;
+ CompWindow *w;
+ CompWindow *focus = NULL;
+
+ for (s = d->screens; s; s = s->next)
+ {
+ for (w = s->reverseWindows; w; w = w->prev)
+ {
+ if (w->type & CompWindowTypeDockMask)
+ continue;
+
+ if ((*s->focusWindow) (w))
+ {
+ if (focus)
+ {
+ if (w->type & (CompWindowTypeNormalMask |
+ CompWindowTypeDialogMask |
+ CompWindowTypeModalDialogMask))
+ {
+ if (w->activeNum > focus->activeNum)
+ focus = w;
+ }
+ }
+ else
+ focus = w;
+ }
+ }
+ }
+
+ if (focus)
+ {
+ if (focus->id != d->activeWindow)
+ moveInputFocusToWindow (focus);
+ }
+ else
+ {
+ XSetInputFocus (d->display, d->screens->root, RevertToPointerRoot,
+ CurrentTime);
+ }
+}
+
+CompScreen *
+findScreenAtDisplay (CompDisplay *d,
+ Window root)
+{
+ CompScreen *s;
+
+ for (s = d->screens; s; s = s->next)
+ {
+ if (s->root == root)
+ return s;
+ }
+
+ return 0;
+}
+
+void
+forEachWindowOnDisplay (CompDisplay *display,
+ ForEachWindowProc proc,
+ void *closure)
+{
+ CompScreen *s;
+
+ for (s = display->screens; s; s = s->next)
+ forEachWindowOnScreen (s, proc, closure);
+}
+
+CompWindow *
+findWindowAtDisplay (CompDisplay *d,
+ Window id)
+{
+ if (lastFoundWindow && lastFoundWindow->id == id)
+ {
+ return lastFoundWindow;
+ }
+ else
+ {
+ CompScreen *s;
+ CompWindow *w;
+
+ for (s = d->screens; s; s = s->next)
+ {
+ w = findWindowAtScreen (s, id);
+ if (w)
+ return w;
+ }
+ }
+
+ return 0;
+}
+
+static CompScreen *
+findScreenForSelection (CompDisplay *display,
+ Window owner,
+ Atom selection)
+{
+ CompScreen *s;
+
+ for (s = display->screens; s; s = s->next)
+ {
+ if (s->wmSnSelectionWindow == owner && s->wmSnAtom == selection)
+ return s;
+ }
+
+ return NULL;
+}
+
+/* from fvwm2, Copyright Matthias Clasen, Dominik Vogt */
+static Bool
+convertProperty (CompDisplay *display,
+ CompScreen *screen,
+ Window w,
+ Atom target,
+ Atom property)
+{
+
+#define N_TARGETS 4
+
+ Atom conversionTargets[N_TARGETS];
+ long icccmVersion[] = { 2, 0 };
+
+ conversionTargets[0] = display->targetsAtom;
+ conversionTargets[1] = display->multipleAtom;
+ conversionTargets[2] = display->timestampAtom;
+ conversionTargets[3] = display->versionAtom;
+
+ if (target == display->targetsAtom)
+ XChangeProperty (display->display, w, property,
+ XA_ATOM, 32, PropModeReplace,
+ (unsigned char *) conversionTargets, N_TARGETS);
+ else if (target == display->timestampAtom)
+ XChangeProperty (display->display, w, property,
+ XA_INTEGER, 32, PropModeReplace,
+ (unsigned char *) &screen->wmSnTimestamp, 1);
+ else if (target == display->versionAtom)
+ XChangeProperty (display->display, w, property,
+ XA_INTEGER, 32, PropModeReplace,
+ (unsigned char *) icccmVersion, 2);
+ else
+ return FALSE;
+
+ /* Be sure the PropertyNotify has arrived so we
+ * can send SelectionNotify
+ */
+ XSync (display->display, FALSE);
+
+ return TRUE;
+}
+
+/* from fvwm2, Copyright Matthias Clasen, Dominik Vogt */
+void
+handleSelectionRequest (CompDisplay *display,
+ XEvent *event)
+{
+ XSelectionEvent reply;
+ CompScreen *screen;
+
+ screen = findScreenForSelection (display,
+ event->xselectionrequest.owner,
+ event->xselectionrequest.selection);
+ if (!screen)
+ return;
+
+ reply.type = SelectionNotify;
+ reply.display = display->display;
+ reply.requestor = event->xselectionrequest.requestor;
+ reply.selection = event->xselectionrequest.selection;
+ reply.target = event->xselectionrequest.target;
+ reply.property = None;
+ reply.time = event->xselectionrequest.time;
+
+ if (event->xselectionrequest.target == display->multipleAtom)
+ {
+ if (event->xselectionrequest.property != None)
+ {
+ Atom type, *adata;
+ int i, format;
+ unsigned long num, rest;
+ unsigned char *data;
+
+ if (XGetWindowProperty (display->display,
+ event->xselectionrequest.requestor,
+ event->xselectionrequest.property,
+ 0, 256, FALSE,
+ display->atomPairAtom,
+ &type, &format, &num, &rest,
+ &data) != Success)
+ return;
+
+ /* FIXME: to be 100% correct, should deal with rest > 0,
+ * but since we have 4 possible targets, we will hardly ever
+ * meet multiple requests with a length > 8
+ */
+ adata = (Atom *) data;
+ i = 0;
+ while (i < (int) num)
+ {
+ if (!convertProperty (display, screen,
+ event->xselectionrequest.requestor,
+ adata[i], adata[i + 1]))
+ adata[i + 1] = None;
+
+ i += 2;
+ }
+
+ XChangeProperty (display->display,
+ event->xselectionrequest.requestor,
+ event->xselectionrequest.property,
+ display->atomPairAtom,
+ 32, PropModeReplace, data, num);
+ }
+ }
+ else
+ {
+ if (event->xselectionrequest.property == None)
+ event->xselectionrequest.property = event->xselectionrequest.target;
+
+ if (convertProperty (display, screen,
+ event->xselectionrequest.requestor,
+ event->xselectionrequest.target,
+ event->xselectionrequest.property))
+ reply.property = event->xselectionrequest.property;
+ }
+
+ XSendEvent (display->display,
+ event->xselectionrequest.requestor,
+ FALSE, 0L, (XEvent *) &reply);
+}
+
+void
+handleSelectionClear (CompDisplay *display,
+ XEvent *event)
+{
+ /* We need to unmanage the screen on which we lost the selection */
+ CompScreen *screen;
+
+ screen = findScreenForSelection (display,
+ event->xselectionclear.window,
+ event->xselectionclear.selection);
+
+ if (!screen)
+ return;
+
+ /* removeScreen (screen); */
+
+ exit (0);
+}
diff --git a/src/event.c b/src/event.c
new file mode 100644
index 00000000..f97f5581
--- /dev/null
+++ b/src/event.c
@@ -0,0 +1,1005 @@
+/*
+ * 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 <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/extensions/shape.h>
+#include <X11/extensions/Xrandr.h>
+#include <X11/extensions/Xevie.h>
+
+#include <compiz.h>
+
+static void
+handleWindowDamageRect (CompWindow *w,
+ int x,
+ int y,
+ int width,
+ int height)
+{
+ REGION region;
+ Bool initial = FALSE;
+
+ if (!w->damaged)
+ {
+ w->damaged = initial = TRUE;
+ w->invisible = WINDOW_INVISIBLE (w);
+ }
+
+ region.extents.x1 = x;
+ region.extents.y1 = y;
+ region.extents.x2 = region.extents.x1 + width;
+ region.extents.y2 = region.extents.y1 + height;
+
+ if (!(*w->screen->damageWindowRect) (w, initial, &region.extents))
+ {
+ region.extents.x1 += w->attrib.x + w->attrib.border_width;
+ region.extents.y1 += w->attrib.y + w->attrib.border_width;
+ region.extents.x2 += w->attrib.x + w->attrib.border_width;
+ region.extents.y2 += w->attrib.y + w->attrib.border_width;
+
+ region.rects = &region.extents;
+ region.numRects = region.size = 1;
+
+ damageWindowRegion (w, &region);
+
+ if (initial)
+ damageWindowOutputExtents (w);
+ }
+}
+
+void
+handleSyncAlarm (CompWindow *w)
+{
+ if (w->syncWait)
+ {
+ if (w->syncWaitHandle)
+ {
+ compRemoveTimeout (w->syncWaitHandle);
+ w->syncWaitHandle = 0;
+ }
+
+ if (resizeWindow (w,
+ w->syncX, w->syncY,
+ w->syncWidth, w->syncHeight,
+ w->syncBorderWidth))
+ {
+ XRectangle *rects;
+ int nDamage;
+
+ nDamage = w->nDamage;
+ rects = w->damageRects;
+ while (nDamage--)
+ {
+ handleWindowDamageRect (w,
+ rects[nDamage].x,
+ rects[nDamage].y,
+ rects[nDamage].width,
+ rects[nDamage].height);
+ }
+
+ w->nDamage = 0;
+ w->syncWait = FALSE;
+ }
+ }
+}
+
+static void
+moveInputFocusToOtherWindow (CompWindow *w)
+{
+ CompDisplay *display = w->screen->display;
+
+ if (w->id == display->activeWindow)
+ {
+ CompWindow *ancestor;
+
+ if (w->transientFor && w->transientFor != w->screen->root)
+ {
+ ancestor = findWindowAtDisplay (display, w->transientFor);
+ if (ancestor && !(ancestor->type & (CompWindowTypeDesktopMask |
+ CompWindowTypeDockMask)))
+ {
+ moveInputFocusToWindow (ancestor);
+ }
+ else
+ focusDefaultWindow (display);
+ }
+ else if (w->type & (CompWindowTypeDialogMask |
+ CompWindowTypeModalDialogMask))
+ {
+ CompWindow *a, *focus = NULL;
+
+ for (a = w->screen->reverseWindows; a; a = a->prev)
+ {
+ if (a->clientLeader == w->clientLeader)
+ {
+ if ((*w->screen->focusWindow) (a))
+ {
+ if (focus)
+ {
+ if (a->type & (CompWindowTypeNormalMask |
+ CompWindowTypeDialogMask |
+ CompWindowTypeModalDialogMask))
+ {
+ if (a->activeNum > focus->activeNum)
+ focus = a;
+ }
+ }
+ else
+ focus = a;
+ }
+ }
+ }
+
+ if (focus && !(focus->type & (CompWindowTypeDesktopMask |
+ CompWindowTypeDockMask)))
+ {
+ moveInputFocusToWindow (focus);
+ }
+ else
+ focusDefaultWindow (display);
+ }
+ else
+ focusDefaultWindow (display);
+ }
+}
+
+static Bool
+autoRaiseTimeout (void *closure)
+{
+ CompDisplay *display = closure;
+
+ if (display->autoRaiseWindow == display->activeWindow)
+ {
+ CompWindow *w;
+
+ w = findWindowAtDisplay (display, display->autoRaiseWindow);
+ if (w)
+ updateWindowAttributes (w);
+ }
+
+ return FALSE;
+}
+
+void
+handleEvent (CompDisplay *display,
+ XEvent *event)
+{
+ CompScreen *s;
+ CompWindow *w;
+
+ switch (event->type) {
+ case Expose:
+ s = findScreenAtDisplay (display, event->xexpose.window);
+ if (s)
+ {
+ int more = event->xexpose.count + 1;
+
+ if (s->nExpose == s->sizeExpose)
+ {
+ if (s->exposeRects)
+ {
+ s->exposeRects = realloc (s->exposeRects,
+ (s->sizeExpose + more) *
+ sizeof (XRectangle));
+ s->sizeExpose += more;
+ }
+ else
+ {
+ s->exposeRects = malloc (more * sizeof (XRectangle));
+ s->sizeExpose = more;
+ }
+ }
+
+ s->exposeRects[s->nExpose].x = event->xexpose.x;
+ s->exposeRects[s->nExpose].y = event->xexpose.y;
+ s->exposeRects[s->nExpose].width = event->xexpose.width;
+ s->exposeRects[s->nExpose].height = event->xexpose.height;
+ s->nExpose++;
+
+ if (event->xexpose.count == 0)
+ {
+ REGION rect;
+
+ rect.rects = &rect.extents;
+ rect.numRects = rect.size = 1;
+
+ while (s->nExpose--)
+ {
+ rect.extents.x1 = s->exposeRects[s->nExpose].x;
+ rect.extents.y1 = s->exposeRects[s->nExpose].y;
+ rect.extents.x2 = rect.extents.x1 +
+ s->exposeRects[s->nExpose].width;
+ rect.extents.y2 = rect.extents.y1 +
+ s->exposeRects[s->nExpose].height;
+
+ damageScreenRegion (s, &rect);
+ }
+ s->nExpose = 0;
+ }
+ }
+ break;
+ case SelectionRequest:
+ handleSelectionRequest (display, event);
+ break;
+ case SelectionClear:
+ handleSelectionClear (display, event);
+ break;
+ case ConfigureNotify:
+ w = findWindowAtDisplay (display, event->xconfigure.window);
+ if (w)
+ {
+ configureWindow (w, &event->xconfigure);
+ }
+ else
+ {
+ s = findScreenAtDisplay (display, event->xconfigure.window);
+ if (s)
+ configureScreen (s, &event->xconfigure);
+ }
+ break;
+ case CreateNotify:
+ s = findScreenAtDisplay (display, event->xcreatewindow.parent);
+ if (s)
+ {
+ addWindow (s, event->xcreatewindow.window,
+ s->reverseWindows ? s->reverseWindows->id : 0);
+ }
+ break;
+ case DestroyNotify:
+ w = findWindowAtDisplay (display, event->xdestroywindow.window);
+ if (w)
+ {
+ destroyWindow (w);
+ moveInputFocusToOtherWindow (w);
+ }
+ break;
+ case MapNotify:
+ w = findWindowAtDisplay (display, event->xmap.window);
+ if (w)
+ mapWindow (w);
+ break;
+ case UnmapNotify:
+ w = findWindowAtDisplay (display, event->xunmap.window);
+ if (w)
+ {
+ unmapWindow (w);
+ moveInputFocusToOtherWindow (w);
+ }
+ break;
+ case ReparentNotify:
+ s = findScreenAtDisplay (display, event->xreparent.parent);
+ if (s)
+ {
+ addWindow (s, event->xreparent.window, 0);
+ }
+ else
+ {
+ w = findWindowAtDisplay (display, event->xreparent.window);
+ if (w)
+ {
+ destroyWindow (w);
+ moveInputFocusToOtherWindow (w);
+ }
+ }
+ break;
+ case CirculateNotify:
+ w = findWindowAtDisplay (display, event->xcirculate.window);
+ if (w)
+ circulateWindow (w, &event->xcirculate);
+ break;
+ case ButtonPress:
+ if (!display->screens->maxGrab)
+ XAllowEvents (display->display, ReplayPointer, event->xbutton.time);
+
+ s = findScreenAtDisplay (display, event->xbutton.root);
+ if (s)
+ {
+ if (event->xbutton.button == Button1 ||
+ event->xbutton.button == Button3)
+ {
+ w = findTopLevelWindowAtScreen (s, event->xbutton.window);
+ if (w)
+ activateWindow (w);
+ }
+
+ if (EV_BUTTON (&s->opt[COMP_SCREEN_OPTION_CLOSE_WINDOW], event))
+ closeActiveWindow (s);
+
+ if (EV_BUTTON (&s->opt[COMP_SCREEN_OPTION_MAIN_MENU], event))
+ panelAction (s, s->display->panelActionMainMenuAtom);
+
+ if (EV_BUTTON (&s->opt[COMP_SCREEN_OPTION_RUN_DIALOG], event))
+ panelAction (s, s->display->panelActionRunDialogAtom);
+
+ if (EV_BUTTON (&s->opt[COMP_SCREEN_OPTION_RUN_COMMAND0], event))
+ runCommand (s, s->opt[COMP_SCREEN_OPTION_COMMAND0].value.s);
+
+ if (EV_BUTTON (&s->opt[COMP_SCREEN_OPTION_RUN_COMMAND1], event))
+ runCommand (s, s->opt[COMP_SCREEN_OPTION_COMMAND1].value.s);
+ }
+ break;
+ case ButtonRelease:
+ break;
+ case KeyPress:
+ s = findScreenAtDisplay (display, event->xkey.root);
+ if (s)
+ {
+ if (EV_KEY (&s->opt[COMP_SCREEN_OPTION_CLOSE_WINDOW], event))
+ closeActiveWindow (s);
+
+ if (EV_KEY (&s->opt[COMP_SCREEN_OPTION_MAIN_MENU], event))
+ panelAction (s, s->display->panelActionMainMenuAtom);
+
+ if (EV_KEY (&s->opt[COMP_SCREEN_OPTION_RUN_DIALOG], event))
+ panelAction (s, s->display->panelActionRunDialogAtom);
+
+ if (EV_KEY (&s->opt[COMP_SCREEN_OPTION_RUN_COMMAND0], event))
+ runCommand (s, s->opt[COMP_SCREEN_OPTION_COMMAND0].value.s);
+
+ if (EV_KEY (&s->opt[COMP_SCREEN_OPTION_RUN_COMMAND1], event))
+ runCommand (s, s->opt[COMP_SCREEN_OPTION_COMMAND1].value.s);
+ }
+ break;
+ case KeyRelease:
+ break;
+ case PropertyNotify:
+ if (event->xproperty.atom == display->winActiveAtom)
+ {
+ Window newActiveWindow;
+
+ newActiveWindow = getActiveWindow (display, event->xproperty.window);
+ if (newActiveWindow != display->activeWindow)
+ {
+ display->activeWindow = newActiveWindow;
+
+ s = findScreenAtDisplay (display, event->xproperty.window);
+ if (s)
+ {
+ w = findWindowAtDisplay (display, newActiveWindow);
+ if (w)
+ w->activeNum = s->activeNum++;
+ }
+ }
+ }
+ else if (event->xproperty.atom == display->winTypeAtom)
+ {
+ w = findWindowAtDisplay (display, event->xproperty.window);
+ if (w)
+ {
+ unsigned int type;
+
+ type = getWindowType (display, w->id);
+
+ if (type != w->wmType)
+ {
+ if (w->attrib.map_state == IsViewable)
+ {
+ if (w->type == CompWindowTypeDesktopMask)
+ w->screen->desktopWindowCount--;
+ else if (type == CompWindowTypeDesktopMask)
+ w->screen->desktopWindowCount++;
+ }
+
+ w->wmType = type;
+
+ recalcWindowType (w);
+
+ if (w->type & CompWindowTypeDesktopMask)
+ w->paint.opacity = OPAQUE;
+ }
+ }
+ }
+ else if (event->xproperty.atom == display->winStateAtom)
+ {
+ w = findWindowAtDisplay (display, event->xproperty.window);
+ if (w)
+ {
+ unsigned int state;
+
+ state = getWindowState (display, w->id);
+
+ if (state != w->state)
+ {
+ w->state = state;
+
+ recalcWindowType (w);
+
+ if (w->type & CompWindowTypeDesktopMask)
+ w->paint.opacity = OPAQUE;
+ }
+ }
+ }
+ else if (event->xproperty.atom == XA_WM_NORMAL_HINTS)
+ {
+ w = findWindowAtDisplay (display, event->xproperty.window);
+ if (w)
+ updateNormalHints (w);
+ }
+ else if (event->xproperty.atom == XA_WM_HINTS)
+ {
+ w = findWindowAtDisplay (display, event->xproperty.window);
+ if (w)
+ updateWmHints (w);
+ }
+ else if (event->xproperty.atom == XA_WM_TRANSIENT_FOR)
+ {
+ w = findWindowAtDisplay (display, event->xproperty.window);
+ if (w)
+ XGetTransientForHint (display->display,
+ w->id, &w->transientFor);
+ }
+ else if (event->xproperty.atom == display->wmClientLeaderAtom)
+ {
+ w = findWindowAtDisplay (display, event->xproperty.window);
+ if (w)
+ w->clientLeader = getClientLeader (w);
+ }
+ else if (event->xproperty.atom == display->winOpacityAtom)
+ {
+ w = findWindowAtDisplay (display, event->xproperty.window);
+ if (w && (w->type & CompWindowTypeDesktopMask) == 0)
+ {
+ GLushort opacity;
+
+ opacity = getWindowProp32 (display, w->id,
+ display->winOpacityAtom,
+ OPAQUE);
+ if (opacity != w->paint.opacity)
+ {
+ w->paint.opacity = opacity;
+ addWindowDamage (w);
+ }
+ }
+ }
+ else if (event->xproperty.atom == display->winBrightnessAtom)
+ {
+ w = findWindowAtDisplay (display, event->xproperty.window);
+ if (w)
+ {
+ GLushort brightness;
+
+ brightness = getWindowProp32 (display, w->id,
+ display->winBrightnessAtom,
+ BRIGHT);
+ if (brightness != w->paint.brightness)
+ {
+ w->paint.brightness = brightness;
+ addWindowDamage (w);
+ }
+ }
+ }
+ else if (event->xproperty.atom == display->winSaturationAtom)
+ {
+ w = findWindowAtDisplay (display, event->xproperty.window);
+ if (w && w->screen->canDoSaturated)
+ {
+ GLushort saturation;
+
+ saturation = getWindowProp32 (display, w->id,
+ display->winSaturationAtom,
+ COLOR);
+ if (saturation != w->saturation)
+ {
+ w->saturation = saturation;
+ if (w->alive)
+ {
+ w->paint.saturation = w->saturation;
+ addWindowDamage (w);
+ }
+ }
+ }
+ }
+ else if (event->xproperty.atom == display->xBackgroundAtom[0] ||
+ event->xproperty.atom == display->xBackgroundAtom[1])
+ {
+ s = findScreenAtDisplay (display, event->xproperty.window);
+ if (s)
+ {
+ finiTexture (s, &s->backgroundTexture);
+ initTexture (s, &s->backgroundTexture);
+
+ damageScreen (s);
+ }
+ }
+ else if (event->xproperty.atom == display->wmStrutAtom ||
+ event->xproperty.atom == display->wmStrutPartialAtom)
+ {
+ w = findWindowAtDisplay (display, event->xproperty.window);
+ if (w)
+ {
+ if (updateWindowStruts (w))
+ updateWorkareaForScreen (w->screen);
+ }
+ }
+ else if (event->xproperty.atom == display->mwmHintsAtom)
+ {
+ w = findWindowAtDisplay (display, event->xproperty.window);
+ if (w)
+ {
+ w->mwmDecor = getMwmDecor (w->screen->display, w->id);
+
+ updateWindowAttributes (w);
+ }
+ }
+ else if (event->xproperty.atom == display->wmProtocolsAtom)
+ {
+ w = findWindowAtDisplay (display, event->xproperty.window);
+ if (w)
+ w->protocols = getProtocols (w->screen->display, w->id);
+ }
+ break;
+ case MotionNotify:
+ break;
+ case ClientMessage:
+ if (event->xclient.message_type == display->winActiveAtom)
+ {
+ w = findWindowAtDisplay (display, event->xclient.window);
+ if (w)
+ activateWindow (w);
+ }
+ else if (event->xclient.message_type == display->winOpacityAtom)
+ {
+ w = findWindowAtDisplay (display, event->xclient.window);
+ if (w && (w->type & CompWindowTypeDesktopMask) == 0)
+ {
+ GLushort opacity;
+
+ opacity = event->xclient.data.l[0] >> 16;
+ if (opacity != w->paint.opacity)
+ {
+ w->paint.opacity = opacity;
+
+ setWindowProp32 (display, w->id, display->winOpacityAtom,
+ w->paint.opacity);
+ addWindowDamage (w);
+ }
+ }
+ }
+ else if (event->xclient.message_type == display->winBrightnessAtom)
+ {
+ w = findWindowAtDisplay (display, event->xclient.window);
+ if (w)
+ {
+ GLushort brightness;
+
+ brightness = event->xclient.data.l[0] >> 16;
+ if (brightness != w->paint.brightness)
+ {
+ w->paint.brightness = brightness;
+
+ setWindowProp32 (display, w->id, display->winBrightnessAtom,
+ w->paint.brightness);
+ addWindowDamage (w);
+ }
+ }
+ }
+ else if (event->xclient.message_type == display->winSaturationAtom)
+ {
+ w = findWindowAtDisplay (display, event->xclient.window);
+ if (w && w->screen->canDoSaturated)
+ {
+ GLushort saturation;
+
+ saturation = event->xclient.data.l[0] >> 16;
+ if (saturation != w->saturation)
+ {
+ w->saturation = saturation;
+
+ setWindowProp32 (display, w->id, display->winSaturationAtom,
+ w->saturation);
+
+ if (w->alive)
+ {
+ w->paint.saturation = w->saturation;
+ addWindowDamage (w);
+ }
+ }
+ }
+ }
+ else if (event->xclient.message_type == display->winStateAtom)
+ {
+ w = findWindowAtDisplay (display, event->xclient.window);
+ if (w)
+ {
+ unsigned long wState, state;
+ int i;
+
+ wState = w->state;
+
+ for (i = 1; i < 3; i++)
+ {
+ state = windowStateMask (display, event->xclient.data.l[i]);
+ if (state & ~CompWindowStateHiddenMask)
+ {
+
+#define _NET_WM_STATE_REMOVE 0
+#define _NET_WM_STATE_ADD 1
+#define _NET_WM_STATE_TOGGLE 2
+
+ switch (event->xclient.data.l[0]) {
+ case _NET_WM_STATE_REMOVE:
+ wState &= ~state;
+ break;
+ case _NET_WM_STATE_ADD:
+ wState |= state;
+ break;
+ case _NET_WM_STATE_TOGGLE:
+ wState ^= state;
+ break;
+ }
+ }
+ }
+
+ if (wState != w->state)
+ {
+ w->state = wState;
+
+ recalcWindowType (w);
+
+ if (!w->attrib.override_redirect)
+ updateWindowAttributes (w);
+
+ setWindowState (display, wState, w->id);
+ }
+ }
+ }
+ else if (event->xclient.message_type == display->wmProtocolsAtom)
+ {
+ if (event->xclient.data.l[0] == display->wmPingAtom)
+ {
+ w = findWindowAtDisplay (display, event->xclient.data.l[2]);
+ if (w)
+ {
+ if (!w->alive)
+ {
+ w->alive = TRUE;
+ w->paint.saturation = w->saturation;
+
+ addWindowDamage (w);
+ }
+ w->lastPong = display->lastPing;
+ }
+ }
+ }
+ else if (event->xclient.message_type == display->closeWindowAtom)
+ {
+ w = findWindowAtDisplay (display, event->xclient.window);
+ if (w)
+ closeWindow (w);
+ }
+ else if (event->xclient.message_type == display->desktopGeometryAtom)
+ {
+ s = findScreenAtDisplay (display, event->xclient.window);
+ if (s)
+ {
+ CompOptionValue value;
+
+ value.i = event->xclient.data.l[0] / s->width;
+
+ (*s->setScreenOption) (s, "size", &value);
+ }
+ }
+ else if (event->xclient.message_type == display->moveResizeWindowAtom)
+ {
+ unsigned int xwcm = 0;
+ XWindowChanges xwc;
+
+ if (event->xclient.data.l[0] & (1 << 7))
+ {
+ xwcm |= CWX;
+ xwc.x = event->xclient.data.l[1];
+ }
+
+ if (event->xclient.data.l[0] & (1 << 8))
+ {
+ xwcm |= CWY;
+ xwc.y = event->xclient.data.l[2];
+ }
+
+ if (event->xclient.data.l[0] & (1 << 9))
+ {
+ xwcm |= CWWidth;
+ xwc.width = event->xclient.data.l[3];
+ }
+
+ if (event->xclient.data.l[0] & (1 << 10))
+ {
+ xwcm |= CWHeight;
+ xwc.height = event->xclient.data.l[4];
+ }
+
+ /* TODO: gravity */
+
+ if (xwcm & (CWX | CWY))
+ {
+ w = findWindowAtDisplay (display, event->xclient.window);
+ if (w)
+ {
+ if (xwcm & CWX)
+ xwc.x += w->input.left;
+
+ if (xwcm & CWY)
+ xwc.y += w->input.top;
+ }
+ }
+
+ XConfigureWindow (display->display,
+ event->xclient.window,
+ xwcm, &xwc);
+ }
+ else if (event->xclient.message_type == display->wmChangeStateAtom)
+ {
+ w = findWindowAtDisplay (display, event->xclient.window);
+ if (w && w->type & CompWindowTypeNormalMask)
+ {
+ if (event->xclient.data.l[0] == IconicState)
+ minimizeWindow (w);
+ else if (event->xclient.data.l[0] == NormalState)
+ unminimizeWindow (w);
+ }
+ }
+ else if (event->xclient.message_type == display->showingDesktopAtom)
+ {
+ s = findScreenAtDisplay (display, event->xclient.window);
+ if (s)
+ {
+ if (event->xclient.data.l[0])
+ enterShowDesktopMode (s);
+ else
+ leaveShowDesktopMode (s);
+ }
+ }
+ break;
+ case MappingNotify:
+ updateModifierMappings (display);
+ break;
+ case MapRequest:
+ w = findWindowAtDisplay (display, event->xmaprequest.window);
+ if (w)
+ {
+ if (w->minimized)
+ unminimizeWindow (w);
+
+ if (w->screen->showingDesktopMask)
+ leaveShowDesktopMode (w->screen);
+
+ if (!(w->state & CompWindowStateHiddenMask))
+ {
+ XMapWindow (display->display, event->xmaprequest.window);
+
+ updateWindowAttributes (w);
+
+ if (!(w->type & (CompWindowTypeDesktopMask |
+ CompWindowTypeDockMask)))
+ moveInputFocusToWindow (w);
+ }
+ }
+ else
+ {
+ XMapWindow (display->display, event->xmaprequest.window);
+ }
+ break;
+ case ConfigureRequest: {
+ unsigned int xwcm;
+ XWindowChanges xwc;
+
+ xwcm = event->xconfigurerequest.value_mask &
+ (CWX | CWY | CWWidth | CWHeight | CWBorderWidth);
+
+ xwc.x = event->xconfigurerequest.x;
+ xwc.y = event->xconfigurerequest.y;
+ xwc.width = event->xconfigurerequest.width;
+ xwc.height = event->xconfigurerequest.height;
+ xwc.border_width = event->xconfigurerequest.border_width;
+
+ /* TODO: gravity */
+
+ if (xwcm & (CWX | CWY))
+ {
+ w = findWindowAtDisplay (display, event->xconfigurerequest.window);
+ if (w)
+ {
+ xwc.x += w->input.left;
+ xwc.y += w->input.top;
+ }
+ }
+
+ XConfigureWindow (display->display,
+ event->xconfigurerequest.window,
+ xwcm, &xwc);
+ } break;
+ case CirculateRequest:
+ break;
+ case FocusIn:
+ if (event->xfocus.window != display->activeWindow &&
+ event->xfocus.mode != NotifyGrab)
+ {
+ w = findWindowAtDisplay (display, event->xfocus.window);
+ if (w)
+ {
+ XChangeProperty (display->display, w->screen->root,
+ display->winActiveAtom,
+ XA_WINDOW, 32, PropModeReplace,
+ (unsigned char *) &w->id, 1);
+ }
+ }
+ break;
+ case EnterNotify:
+ if (!display->screens->maxGrab &&
+ event->xcrossing.mode != NotifyGrab &&
+ event->xcrossing.detail != NotifyInferior)
+ {
+ Bool raise;
+ int delay;
+
+ raise = display->opt[COMP_DISPLAY_OPTION_AUTORAISE].value.b;
+ delay = display->opt[COMP_DISPLAY_OPTION_AUTORAISE_DELAY].value.i;
+
+ s = findScreenAtDisplay (display, event->xcrossing.root);
+ if (s)
+ w = findTopLevelWindowAtScreen (s, event->xcrossing.window);
+ else
+ w = NULL;
+
+ if (w && w->id != display->below)
+ {
+ display->below = w->id;
+
+ if (!display->opt[COMP_DISPLAY_OPTION_CLICK_TO_FOCUS].value.b)
+ {
+ if (display->autoRaiseHandle &&
+ display->autoRaiseWindow != w->id)
+ {
+ compRemoveTimeout (display->autoRaiseHandle);
+ display->autoRaiseHandle = 0;
+ }
+
+ if (w->type & ~(CompWindowTypeDockMask |
+ CompWindowTypeDesktopMask))
+ {
+ moveInputFocusToWindow (w);
+
+ if (raise)
+ {
+ if (delay > 0)
+ {
+ display->autoRaiseWindow = w->id;
+ display->autoRaiseHandle =
+ compAddTimeout (delay, autoRaiseTimeout,
+ display);
+ }
+ else
+ updateWindowAttributes (w);
+ }
+ }
+ }
+ }
+ }
+ break;
+ case LeaveNotify:
+ if (event->xcrossing.detail != NotifyInferior)
+ {
+ if (event->xcrossing.window == display->below)
+ display->below = None;
+ }
+ break;
+ default:
+ if (event->type == display->damageEvent + XDamageNotify)
+ {
+ XDamageNotifyEvent *de = (XDamageNotifyEvent *) event;
+
+ if (lastDamagedWindow && de->drawable == lastDamagedWindow->id)
+ {
+ w = lastDamagedWindow;
+ }
+ else
+ {
+ w = findWindowAtDisplay (display, de->drawable);
+ if (w)
+ lastDamagedWindow = w;
+ }
+
+ if (w)
+ {
+ if (w->syncWait)
+ {
+ if (w->nDamage == w->sizeDamage)
+ {
+ if (w->damageRects)
+ {
+ w->damageRects = realloc (w->damageRects,
+ (w->sizeDamage + 1) *
+ sizeof (XRectangle));
+ w->sizeDamage += 1;
+ }
+ else
+ {
+ w->damageRects = malloc (sizeof (XRectangle));
+ w->sizeDamage = 1;
+ }
+ }
+
+ w->damageRects[w->nDamage].x = de->area.x;
+ w->damageRects[w->nDamage].y = de->area.y;
+ w->damageRects[w->nDamage].width = de->area.width;
+ w->damageRects[w->nDamage].height = de->area.height;
+ w->nDamage++;
+ }
+ else
+ {
+ handleWindowDamageRect (w,
+ de->area.x,
+ de->area.y,
+ de->area.width,
+ de->area.height);
+ }
+ }
+ }
+ else if (display->shapeExtension &&
+ event->type == display->shapeEvent + ShapeNotify)
+ {
+ w = findWindowAtDisplay (display, ((XShapeEvent *) event)->window);
+ if (w)
+ {
+ if (w->mapNum)
+ {
+ addWindowDamage (w);
+ updateWindowRegion (w);
+ addWindowDamage (w);
+ }
+ }
+ }
+ else if (event->type == display->randrEvent + RRScreenChangeNotify)
+ {
+ XRRScreenChangeNotifyEvent *rre;
+
+ rre = (XRRScreenChangeNotifyEvent *) event;
+
+ s = findScreenAtDisplay (display, rre->root);
+ if (s)
+ detectRefreshRateOfScreen (s);
+ }
+ else if (event->type == display->syncEvent + XSyncAlarmNotify)
+ {
+ XSyncAlarmNotifyEvent *sa;
+
+ sa = (XSyncAlarmNotifyEvent *) event;
+
+ w = NULL;
+
+ for (s = display->screens; s; s = s->next)
+ for (w = s->windows; w; w = w->next)
+ if (w->syncAlarm == sa->alarm)
+ break;
+
+ if (w)
+ handleSyncAlarm (w);
+ }
+ break;
+ }
+}
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 00000000..b715f42f
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,211 @@
+/*
+ * 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 <signal.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/wait.h>
+
+#include <compiz.h>
+
+char *programName;
+char **programArgv;
+int programArgc;
+
+char *backgroundImage = "background.png";
+char *windowImage = "window.png";
+
+REGION emptyRegion;
+REGION infiniteRegion;
+GLushort defaultColor[4] = { 0, 0, 0, 0 };
+Window currentRoot = 0;
+
+int defaultRefreshRate = 50;
+char *defaultTextureFilter = "Good";
+
+char *windowTypeString[] = {
+ "Desktop",
+ "Dock",
+ "Toolbar",
+ "Menu",
+ "Utility",
+ "Splash",
+ "Dialog",
+ "ModalDialog",
+ "Normal",
+ "Fullscreen",
+ "Unknown"
+};
+int nWindowTypeString =
+ sizeof (windowTypeString) / sizeof (windowTypeString[0]);
+
+Bool testMode = FALSE;
+Bool restartSignal = FALSE;
+
+CompWindow *lastFoundWindow = 0;
+CompWindow *lastDamagedWindow = 0;
+
+Bool replaceCurrentWm = FALSE;
+
+static void
+usage (void)
+{
+ printf ("Usage: %s "
+ "[--display DISPLAY] "
+ "[--bg-image PNG] "
+ "[--window-image PNG]\n "
+ "[--refresh-rate RATE] "
+ "[--fast-filter] "
+ "[--test-mode] "
+ "[--replace]\n "
+ "[--sm-disable] "
+ "[--sm-save-file] "
+ "[--sm-client-id] "
+ "[--help] "
+ "[PLUGIN]...\n",
+ programName);
+}
+
+static void
+signalHandler (int sig)
+{
+ int status;
+
+ switch (sig) {
+ case SIGCHLD:
+ waitpid (-1, &status, WNOHANG | WUNTRACED);
+ break;
+ case SIGHUP:
+ restartSignal = TRUE;
+ default:
+ break;
+ }
+}
+
+int
+main (int argc, char **argv)
+{
+ char *displayName = 0;
+ char *plugin[256];
+ int i, nPlugin = 0;
+ Bool disableSm = TRUE;
+ char *clientId = NULL;
+ char *saveFile = NULL;
+
+ programName = argv[0];
+ programArgc = argc;
+ programArgv = argv;
+
+ signal (SIGHUP, signalHandler);
+ signal (SIGCHLD, signalHandler);
+
+ emptyRegion.rects = &emptyRegion.extents;
+ emptyRegion.numRects = 0;
+ emptyRegion.extents.x1 = 0;
+ emptyRegion.extents.y1 = 0;
+ emptyRegion.extents.x2 = 0;
+ emptyRegion.extents.y2 = 0;
+ emptyRegion.size = 0;
+
+ infiniteRegion.rects = &infiniteRegion.extents;
+ infiniteRegion.numRects = 1;
+ infiniteRegion.extents.x1 = MINSHORT;
+ infiniteRegion.extents.y1 = MINSHORT;
+ infiniteRegion.extents.x2 = MAXSHORT;
+ infiniteRegion.extents.y2 = MAXSHORT;
+
+ for (i = 1; i < argc; i++)
+ {
+ if (!strcmp (argv[i], "--help"))
+ {
+ usage ();
+ return 0;
+ }
+ else if (!strcmp (argv[i], "--display"))
+ {
+ if (i + 1 < argc)
+ displayName = argv[++i];
+ }
+ else if (!strcmp (argv[i], "--refresh-rate"))
+ {
+ if (i + 1 < argc)
+ {
+ defaultRefreshRate = atoi (programArgv[++i]);
+ defaultRefreshRate = RESTRICT_VALUE (defaultRefreshRate,
+ 1, 1000);
+ }
+ }
+ else if (!strcmp (argv[i], "--fast-filter"))
+ {
+ defaultTextureFilter = "Fast";
+ }
+ else if (!strcmp (argv[i], "--test-mode"))
+ {
+ testMode = TRUE;
+ }
+ else if (!strcmp (argv[i], "--replace"))
+ {
+ replaceCurrentWm = TRUE;
+ }
+ else if (!strcmp (argv[i], "--sm-disable"))
+ {
+ disableSm = TRUE;
+ }
+ else if (!strcmp (argv[i], "--sm-client-id"))
+ {
+ if (i + 1 < argc)
+ clientId = argv[++i];
+ }
+ else if (!strcmp (argv[i], "--sm-save-file"))
+ {
+ if (i + 1 < argc)
+ saveFile = argv[++i];
+ }
+ else if (!strcmp (argv[i], "--bg-image"))
+ {
+ if (i + 1 < argc)
+ backgroundImage = argv[++i];
+ }
+ else if (!strcmp (argv[i], "--window-image"))
+ {
+ if (i + 1 < argc)
+ windowImage = argv[++i];
+ }
+ else
+ {
+ if (nPlugin < 256)
+ plugin[nPlugin++] = argv[i];
+ }
+ }
+
+ if (!addDisplay (displayName, plugin, nPlugin))
+ return 1;
+
+ eventLoop ();
+
+ return 0;
+}
diff --git a/src/option.c b/src/option.c
new file mode 100644
index 00000000..23999c66
--- /dev/null
+++ b/src/option.c
@@ -0,0 +1,292 @@
+/*
+ * 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 <strings.h>
+#include <math.h>
+
+#include <compiz.h>
+
+CompOption *
+compFindOption (CompOption *option,
+ int nOption,
+ char *name,
+ int *index)
+{
+ int i;
+
+ for (i = 0; i < nOption; i++)
+ {
+ if (strcmp (option[i].name, name) == 0)
+ {
+ if (index)
+ *index = i;
+
+ return &option[i];
+ }
+ }
+
+ return 0;
+}
+
+Bool
+compSetBoolOption (CompOption *option,
+ CompOptionValue *value)
+{
+ option->value.i = (value->b) ? TRUE : FALSE;
+
+ return TRUE;
+}
+
+Bool
+compSetIntOption (CompOption *option,
+ CompOptionValue *value)
+{
+ if (value->i < option->rest.i.min ||
+ value->i > option->rest.i.max ||
+ value->i == option->value.i)
+ return FALSE;
+
+ option->value.i = value->i;
+
+ return TRUE;
+}
+
+Bool
+compSetFloatOption (CompOption *option,
+ CompOptionValue *value)
+{
+ float v, p;
+
+ p = 1.0f / option->rest.f.precision;
+ v = ((int) (value->f * p + 0.5f)) / p;
+
+ if (v < option->rest.f.min ||
+ v > option->rest.f.max ||
+ v == option->value.f)
+ return FALSE;
+
+ option->value.f = v;
+
+ return TRUE;
+}
+
+Bool
+compSetStringOption (CompOption *option,
+ CompOptionValue *value)
+{
+ char *s;
+
+ s = value->s;
+ if (!s)
+ s = "";
+
+ if (option->rest.s.nString)
+ {
+ int i;
+
+ for (i = 0; i < option->rest.s.nString; i++)
+ {
+ if (strcmp (option->rest.s.string[i], s) == 0)
+ break;
+ }
+
+ if (i == option->rest.s.nString)
+ s = option->rest.s.string[0];
+ }
+
+ if (option->value.s == s)
+ return FALSE;
+
+ if (option->value.s && s)
+ {
+ if (strcmp (option->value.s, s) == 0)
+ return FALSE;
+ }
+
+ if (option->value.s)
+ free (option->value.s);
+
+ option->value.s = strdup (s);
+
+ return TRUE;
+}
+
+Bool
+compSetColorOption (CompOption *option,
+ CompOptionValue *value)
+{
+ if (memcmp (value->c, option->value.c, sizeof (value->c)) == 0)
+ return FALSE;
+
+ memcpy (option->value.c, value->c, sizeof (value->c));
+
+ return TRUE;
+}
+
+Bool
+compSetBindingOption (CompOption *option,
+ CompOptionValue *value)
+{
+ CompBinding *binding;
+
+ binding = &option->value.bind;
+ if (value->bind.type == CompBindingTypeButton)
+ {
+ if (binding->type == CompBindingTypeButton &&
+ binding->u.button.button == value->bind.u.button.button &&
+ binding->u.button.modifiers == value->bind.u.button.modifiers)
+ return FALSE;
+ }
+ else
+ {
+ if (binding->type == CompBindingTypeKey &&
+ binding->u.key.keycode == value->bind.u.key.keycode &&
+ binding->u.key.modifiers == value->bind.u.key.modifiers)
+ return FALSE;
+ }
+
+ *binding = value->bind;
+
+ return TRUE;
+}
+
+Bool
+compSetOptionList (CompOption *option,
+ CompOptionValue *value)
+{
+ CompOption o;
+ Bool status = FALSE;
+ int i, min;
+
+ if (value->list.nValue != option->value.list.nValue)
+ {
+ CompOptionValue *v;
+
+ v = malloc (sizeof (CompOptionValue) * value->list.nValue);
+ if (!v)
+ return FALSE;
+
+ min = MIN (value->list.nValue, option->value.list.nValue);
+
+ if (min < option->value.list.nValue)
+ {
+ switch (option->value.list.type) {
+ case CompOptionTypeString:
+ for (i = min; i < option->value.list.nValue; i++)
+ {
+ if (option->value.list.value[i].s)
+ free (option->value.list.value[i].s);
+ }
+ default:
+ break;
+ }
+ }
+
+ memset (v, 0, sizeof (CompOptionValue) * value->list.nValue);
+
+ if (min)
+ memcpy (v, option->value.list.value,
+ sizeof (CompOptionValue) * min);
+
+ if (option->value.list.value)
+ free (option->value.list.value);
+
+ option->value.list.value = v;
+ option->value.list.nValue = value->list.nValue;
+
+ status = TRUE;
+ }
+
+ o = *option;
+ o.type = option->value.list.type;
+
+ for (i = 0; i < value->list.nValue; i++)
+ {
+ o.value = option->value.list.value[i];
+
+ switch (o.type) {
+ case CompOptionTypeBool:
+ status |= compSetBoolOption (&o, &value->list.value[i]);
+ break;
+ case CompOptionTypeInt:
+ status |= compSetIntOption (&o, &value->list.value[i]);
+ break;
+ case CompOptionTypeFloat:
+ status |= compSetFloatOption (&o, &value->list.value[i]);
+ break;
+ case CompOptionTypeString:
+ status |= compSetStringOption (&o, &value->list.value[i]);
+ break;
+ case CompOptionTypeColor:
+ status |= compSetColorOption (&o, &value->list.value[i]);
+ break;
+ case CompOptionTypeBinding:
+ status |= compSetBindingOption (&o, &value->list.value[i]);
+ default:
+ break;
+ }
+
+ option->value.list.value[i] = o.value;
+ }
+
+ return status;
+}
+
+unsigned int
+compWindowTypeMaskFromStringList (CompOptionValue *value)
+{
+ int i;
+ unsigned int mask = 0;
+
+ for (i = 0; i < value->list.nValue; i++)
+ {
+ if (strcasecmp (value->list.value[i].s, "desktop") == 0)
+ mask |= CompWindowTypeDesktopMask;
+ else if (strcasecmp (value->list.value[i].s, "dock") == 0)
+ mask |= CompWindowTypeDockMask;
+ else if (strcasecmp (value->list.value[i].s, "toolbar") == 0)
+ mask |= CompWindowTypeToolbarMask;
+ else if (strcasecmp (value->list.value[i].s, "menu") == 0)
+ mask |= CompWindowTypeMenuMask;
+ else if (strcasecmp (value->list.value[i].s, "utility") == 0)
+ mask |= CompWindowTypeUtilMask;
+ else if (strcasecmp (value->list.value[i].s, "splash") == 0)
+ mask |= CompWindowTypeSplashMask;
+ else if (strcasecmp (value->list.value[i].s, "dialog") == 0)
+ mask |= CompWindowTypeDialogMask;
+ else if (strcasecmp (value->list.value[i].s, "modaldialog") == 0)
+ mask |= CompWindowTypeModalDialogMask;
+ else if (strcasecmp (value->list.value[i].s, "normal") == 0)
+ mask |= CompWindowTypeNormalMask;
+ else if (strcasecmp (value->list.value[i].s, "fullscreen") == 0)
+ mask |= CompWindowTypeFullscreenMask;
+ else if (strcasecmp (value->list.value[i].s, "unknown") == 0)
+ mask |= CompWindowTypeUnknownMask;
+ }
+
+ return mask;
+}
diff --git a/src/paint.c b/src/paint.c
new file mode 100644
index 00000000..f329d825
--- /dev/null
+++ b/src/paint.c
@@ -0,0 +1,864 @@
+/*
+ * 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 <math.h>
+
+#include <compiz.h>
+
+ScreenPaintAttrib defaultScreenPaintAttrib = {
+ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -DEFAULT_Z_CAMERA
+};
+
+WindowPaintAttrib defaultWindowPaintAttrib = {
+ OPAQUE, 1.0f, 1.0f
+};
+
+void
+preparePaintScreen (CompScreen *screen,
+ int msSinceLastPaint) {}
+
+void
+donePaintScreen (CompScreen *screen) {}
+
+void
+translateRotateScreen (const ScreenPaintAttrib *sa)
+{
+ glTranslatef (sa->xTranslate,
+ sa->yTranslate,
+ sa->zTranslate + sa->zCamera);
+
+ glRotatef (sa->xRotate, 0.0f, 1.0f, 0.0f);
+ glRotatef (sa->vRotate,
+ 1.0f - sa->xRotate / 90.0f,
+ 0.0f,
+ sa->xRotate / 90.0f);
+ glRotatef (sa->yRotate, 0.0f, 1.0f, 0.0f);
+}
+
+void
+paintTransformedScreen (CompScreen *screen,
+ const ScreenPaintAttrib *sAttrib,
+ unsigned int mask)
+{
+ CompWindow *w;
+ int windowMask;
+ int backgroundMask;
+
+ glPushMatrix ();
+
+ translateRotateScreen (sAttrib);
+
+ glTranslatef (-0.5f, -0.5f, -sAttrib->zTranslate);
+ glScalef (1.0f / screen->width, -1.0f / screen->height, 1.0f);
+ glTranslatef (0.0f, -screen->height, 0.0f);
+
+ if (mask & PAINT_SCREEN_TRANSFORMED_MASK)
+ {
+ windowMask = PAINT_WINDOW_ON_TRANSFORMED_SCREEN_MASK;
+ backgroundMask = PAINT_BACKGROUND_ON_TRANSFORMED_SCREEN_MASK;
+
+ if (mask & PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS_MASK)
+ {
+ backgroundMask |= PAINT_BACKGROUND_WITH_STENCIL_MASK;
+
+ (*screen->paintBackground) (screen, &screen->region,
+ backgroundMask);
+
+ glEnable (GL_STENCIL_TEST);
+
+ for (w = screen->windows; w; w = w->next)
+ {
+ if (w->destroyed || w->attrib.map_state != IsViewable)
+ continue;
+
+ if (w->damaged)
+ (*screen->paintWindow) (w, &w->paint, &screen->region,
+ windowMask);
+ }
+
+ glDisable (GL_STENCIL_TEST);
+
+ glPopMatrix ();
+
+ return;
+ }
+ }
+ else
+ windowMask = backgroundMask = 0;
+
+ (*screen->paintBackground) (screen, &screen->region, backgroundMask);
+
+ for (w = screen->windows; w; w = w->next)
+ {
+ if (w->destroyed || w->attrib.map_state != IsViewable)
+ continue;
+
+ if (w->damaged)
+ (*screen->paintWindow) (w, &w->paint, &screen->region, windowMask);
+ }
+
+ glPopMatrix ();
+}
+
+Bool
+paintScreen (CompScreen *screen,
+ const ScreenPaintAttrib *sAttrib,
+ Region region,
+ unsigned int mask)
+{
+ static Region tmpRegion = NULL;
+ CompWindow *w;
+
+ if (mask & PAINT_SCREEN_REGION_MASK)
+ {
+ if (mask & PAINT_SCREEN_TRANSFORMED_MASK)
+ {
+ if (mask & PAINT_SCREEN_FULL_MASK)
+ {
+ (*screen->paintTransformedScreen) (screen, sAttrib, mask);
+
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+
+ /* fall through and redraw region */
+ }
+ else if (mask & PAINT_SCREEN_FULL_MASK)
+ {
+ (*screen->paintTransformedScreen) (screen, sAttrib, mask);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+
+ glPushMatrix ();
+
+ glTranslatef (-0.5f, -0.5f, -DEFAULT_Z_CAMERA);
+ glScalef (1.0f / screen->width, -1.0f / screen->height, 1.0f);
+ glTranslatef (0.0f, -screen->height, 0.0f);
+
+ if (mask & PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS_MASK)
+ {
+ (*screen->paintBackground) (screen, region, 0);
+
+ /* paint all windows from bottom to top */
+ for (w = screen->windows; w; w = w->next)
+ {
+ if (w->destroyed || w->attrib.map_state != IsViewable)
+ continue;
+
+ (*screen->paintWindow) (w, &w->paint, region, 0);
+ }
+ }
+ else
+ {
+ if (!tmpRegion)
+ {
+ tmpRegion = XCreateRegion ();
+ if (!tmpRegion)
+ return FALSE;
+ }
+
+ XSubtractRegion (region, &emptyRegion, tmpRegion);
+
+ /* paint solid windows */
+ for (w = screen->reverseWindows; w; w = w->prev)
+ {
+ if (w->destroyed || w->invisible)
+ continue;
+
+ if (tmpRegion->numRects)
+ {
+ if ((*screen->paintWindow) (w, &w->paint, tmpRegion,
+ PAINT_WINDOW_SOLID_MASK))
+ XSubtractRegion (tmpRegion, w->region, tmpRegion);
+ }
+
+ /* copy region */
+ XSubtractRegion (tmpRegion, &emptyRegion, w->clip);
+ }
+
+ if (tmpRegion->numRects)
+ (*screen->paintBackground) (screen, tmpRegion, 0);
+
+ /* paint translucent windows */
+ for (w = screen->windows; w; w = w->next)
+ {
+ if (w->destroyed || w->invisible)
+ continue;
+
+ if (w->clip->numRects)
+ (*screen->paintWindow) (w, &w->paint, w->clip,
+ PAINT_WINDOW_TRANSLUCENT_MASK);
+ }
+ }
+
+ glPopMatrix ();
+
+ return TRUE;
+}
+
+#define ADD_RECT(data, m, n, x1, y1, x2, y2) \
+ for (it = 0; it < n; it++) \
+ { \
+ *(data)++ = COMP_TEX_COORD_X (&m[it], x1); \
+ *(data)++ = COMP_TEX_COORD_Y (&m[it], y2); \
+ } \
+ *(data)++ = (x1); \
+ *(data)++ = (y2); \
+ for (it = 0; it < n; it++) \
+ { \
+ *(data)++ = COMP_TEX_COORD_X (&m[it], x2); \
+ *(data)++ = COMP_TEX_COORD_Y (&m[it], y2); \
+ } \
+ *(data)++ = (x2); \
+ *(data)++ = (y2); \
+ for (it = 0; it < n; it++) \
+ { \
+ *(data)++ = COMP_TEX_COORD_X (&m[it], x2); \
+ *(data)++ = COMP_TEX_COORD_Y (&m[it], y1); \
+ } \
+ *(data)++ = (x2); \
+ *(data)++ = (y1); \
+ for (it = 0; it < n; it++) \
+ { \
+ *(data)++ = COMP_TEX_COORD_X (&m[it], x1); \
+ *(data)++ = COMP_TEX_COORD_Y (&m[it], y1); \
+ } \
+ *(data)++ = (x1); \
+ *(data)++ = (y1)
+
+#define ADD_QUAD(data, m, n, x1, y1, x2, y2) \
+ for (it = 0; it < n; it++) \
+ { \
+ *(data)++ = COMP_TEX_COORD_XY (&m[it], x1, y2); \
+ *(data)++ = COMP_TEX_COORD_YX (&m[it], x1, y2); \
+ } \
+ *(data)++ = (x1); \
+ *(data)++ = (y2); \
+ for (it = 0; it < n; it++) \
+ { \
+ *(data)++ = COMP_TEX_COORD_XY (&m[it], x2, y2); \
+ *(data)++ = COMP_TEX_COORD_YX (&m[it], x2, y2); \
+ } \
+ *(data)++ = (x2); \
+ *(data)++ = (y2); \
+ for (it = 0; it < n; it++) \
+ { \
+ *(data)++ = COMP_TEX_COORD_XY (&m[it], x2, y1); \
+ *(data)++ = COMP_TEX_COORD_YX (&m[it], x2, y1); \
+ } \
+ *(data)++ = (x2); \
+ *(data)++ = (y1); \
+ for (it = 0; it < n; it++) \
+ { \
+ *(data)++ = COMP_TEX_COORD_XY (&m[it], x1, y1); \
+ *(data)++ = COMP_TEX_COORD_YX (&m[it], x1, y1); \
+ } \
+ *(data)++ = (x1); \
+ *(data)++ = (y1)
+
+
+Bool
+moreWindowVertices (CompWindow *w,
+ int newSize)
+{
+ if (newSize > w->vertexSize)
+ {
+ GLfloat *vertices;
+
+ vertices = realloc (w->vertices, sizeof (GLfloat) * newSize);
+ if (!vertices)
+ return FALSE;
+
+ w->vertices = vertices;
+ w->vertexSize = newSize;
+ }
+
+ return TRUE;
+}
+
+Bool
+moreWindowIndices (CompWindow *w,
+ int newSize)
+{
+ if (newSize > w->indexSize)
+ {
+ GLushort *indices;
+
+ indices = realloc (w->indices, sizeof (GLushort) * newSize);
+ if (!indices)
+ return FALSE;
+
+ w->indices = indices;
+ w->indexSize = newSize;
+ }
+
+ return TRUE;
+}
+
+void
+addWindowGeometry (CompWindow *w,
+ CompMatrix *matrix,
+ int nMatrix,
+ Region region,
+ Region clip)
+{
+ BoxRec full;
+
+ w->texUnits = nMatrix;
+
+ full = clip->extents;
+ if (region->extents.x1 > full.x1)
+ full.x1 = region->extents.x1;
+ if (region->extents.y1 > full.y1)
+ full.y1 = region->extents.y1;
+ if (region->extents.x2 < full.x2)
+ full.x2 = region->extents.x2;
+ if (region->extents.y2 < full.y2)
+ full.y2 = region->extents.y2;
+
+ if (full.x1 < full.x2 && full.y1 < full.y2)
+ {
+ BoxPtr pBox;
+ int nBox;
+ BoxPtr pClip;
+ int nClip;
+ BoxRec cbox;
+ int vSize;
+ int n, it, x1, y1, x2, y2;
+ GLfloat *d;
+ Bool rect = TRUE;
+
+ for (it = 0; it < nMatrix; it++)
+ {
+ if (matrix[it].xy != 0.0f && matrix[it].yx != 0.0f)
+ {
+ rect = FALSE;
+ break;
+ }
+ }
+
+ pBox = region->rects;
+ nBox = region->numRects;
+
+ vSize = 2 + nMatrix * 2;
+
+ n = w->vCount / 4;
+
+ if ((n + nBox) * vSize * 4 > w->vertexSize)
+ {
+ if (!moreWindowVertices (w, (n + nBox) * vSize * 4))
+ return;
+ }
+
+ d = w->vertices + (w->vCount * vSize);
+
+ while (nBox--)
+ {
+ x1 = pBox->x1;
+ y1 = pBox->y1;
+ x2 = pBox->x2;
+ y2 = pBox->y2;
+
+ pBox++;
+
+ if (x1 < full.x1)
+ x1 = full.x1;
+ if (y1 < full.y1)
+ y1 = full.y1;
+ if (x2 > full.x2)
+ x2 = full.x2;
+ if (y2 > full.y2)
+ y2 = full.y2;
+
+ if (x1 < x2 && y1 < y2)
+ {
+ nClip = clip->numRects;
+
+ if (nClip == 1)
+ {
+ if (rect)
+ {
+ ADD_RECT (d, matrix, nMatrix, x1, y1, x2, y2);
+ }
+ else
+ {
+ ADD_QUAD (d, matrix, nMatrix, x1, y1, x2, y2);
+ }
+
+ n++;
+ }
+ else
+ {
+ pClip = clip->rects;
+
+ if (((n + nClip) * vSize * 4) > w->vertexSize)
+ {
+ if (!moreWindowVertices (w, (n + nClip) * vSize * 4))
+ return;
+
+ d = w->vertices + (n * vSize * 4);
+ }
+
+ while (nClip--)
+ {
+ cbox = *pClip;
+
+ pClip++;
+
+ if (cbox.x1 < x1)
+ cbox.x1 = x1;
+ if (cbox.y1 < y1)
+ cbox.y1 = y1;
+ if (cbox.x2 > x2)
+ cbox.x2 = x2;
+ if (cbox.y2 > y2)
+ cbox.y2 = y2;
+
+ if (cbox.x1 < cbox.x2 && cbox.y1 < cbox.y2)
+ {
+ if (rect)
+ {
+ ADD_RECT (d, matrix, nMatrix,
+ cbox.x1, cbox.y1, cbox.x2, cbox.y2);
+ }
+ else
+ {
+ ADD_QUAD (d, matrix, nMatrix,
+ cbox.x1, cbox.y1, cbox.x2, cbox.y2);
+ }
+
+ n++;
+ }
+ }
+ }
+ }
+ }
+ w->vCount = n * 4;
+ }
+}
+
+void
+drawWindowGeometry (CompWindow *w)
+{
+ int texUnit = w->texUnits;
+ int currentTexUnit = 0;
+ int stride = (1 + texUnit) * 2;
+ GLfloat *vertices = w->vertices + (stride - 2);
+
+ stride *= sizeof (GLfloat);
+
+ glVertexPointer (2, GL_FLOAT, stride, vertices);
+
+ while (texUnit--)
+ {
+ if (texUnit != currentTexUnit)
+ {
+ w->screen->clientActiveTexture (GL_TEXTURE0_ARB + texUnit);
+ currentTexUnit = texUnit;
+ }
+ vertices -= 2;
+ glTexCoordPointer (2, GL_FLOAT, stride, vertices);
+ }
+
+ glDrawArrays (GL_QUADS, 0, w->vCount);
+}
+
+void
+drawWindowTexture (CompWindow *w,
+ CompTexture *texture,
+ const WindowPaintAttrib *attrib,
+ unsigned int mask)
+{
+ int filter;
+
+ glPushMatrix ();
+
+ if (mask & PAINT_WINDOW_TRANSFORMED_MASK)
+ {
+ glTranslatef (w->attrib.x, w->attrib.y, 0.0f);
+ glScalef (attrib->xScale, attrib->yScale, 0.0f);
+ glTranslatef (-w->attrib.x, -w->attrib.y, 0.0f);
+
+ filter = COMP_TEXTURE_FILTER_GOOD;
+ }
+ else if (mask & PAINT_WINDOW_ON_TRANSFORMED_SCREEN_MASK)
+ {
+ filter = COMP_TEXTURE_FILTER_GOOD;
+ }
+ else
+ {
+ filter = COMP_TEXTURE_FILTER_FAST;
+ }
+
+ if (w->screen->canDoSaturated && attrib->saturation != COLOR)
+ {
+ GLfloat constant[4];
+
+ if (mask & PAINT_WINDOW_TRANSLUCENT_MASK)
+ glEnable (GL_BLEND);
+
+ enableTexture (w->screen, texture, filter);
+
+ glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
+
+ glTexEnvf (GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE);
+ glTexEnvf (GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE);
+ glTexEnvf (GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR);
+ glTexEnvf (GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_PRIMARY_COLOR);
+ glTexEnvf (GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
+ glTexEnvf (GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
+ glTexEnvf (GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_ALPHA);
+
+ glTexEnvf (GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
+ glTexEnvf (GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE);
+ glTexEnvf (GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
+
+ glColor4f (1.0f, 1.0f, 1.0f, 0.5f);
+
+ w->screen->activeTexture (GL_TEXTURE1_ARB);
+
+ enableTexture (w->screen, texture, filter);
+
+ glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
+
+ glTexEnvf (GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_DOT3_RGB);
+ glTexEnvf (GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS);
+ glTexEnvf (GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT);
+ glTexEnvf (GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
+ glTexEnvf (GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
+
+ if (w->screen->canDoSlightlySaturated && attrib->saturation > 0)
+ {
+ glTexEnvf (GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
+ glTexEnvf (GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS);
+ glTexEnvf (GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
+
+ constant[0] = 0.5f + 0.5f * RED_SATURATION_WEIGHT;
+ constant[1] = 0.5f + 0.5f * GREEN_SATURATION_WEIGHT;
+ constant[2] = 0.5f + 0.5f * BLUE_SATURATION_WEIGHT;
+ constant[3] = 1.0;
+
+ glTexEnvfv (GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, constant);
+
+ w->screen->activeTexture (GL_TEXTURE2_ARB);
+
+ enableTexture (w->screen, texture, filter);
+
+ glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
+
+ glTexEnvf (GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE);
+ glTexEnvf (GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE0);
+ glTexEnvf (GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PREVIOUS);
+ glTexEnvf (GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_CONSTANT);
+ glTexEnvf (GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
+ glTexEnvf (GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
+ glTexEnvf (GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_ALPHA);
+
+ glTexEnvf (GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
+ glTexEnvf (GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS);
+ glTexEnvf (GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
+
+ constant[3] = attrib->saturation / 65535.0f;
+
+ glTexEnvfv (GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, constant);
+
+ if (attrib->opacity < OPAQUE || attrib->brightness != BRIGHT)
+ {
+ w->screen->activeTexture (GL_TEXTURE3_ARB);
+
+ enableTexture (w->screen, texture, filter);
+
+ constant[3] = attrib->opacity / 65535.0f;
+ constant[0] = constant[1] = constant[2] = constant[3] *
+ attrib->brightness / 65535.0f;
+
+ glTexEnvfv (GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, constant);
+
+ glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
+
+ glTexEnvf (GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
+ glTexEnvf (GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS);
+ glTexEnvf (GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT);
+ glTexEnvf (GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
+ glTexEnvf (GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
+
+ glTexEnvf (GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE);
+ glTexEnvf (GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS);
+ glTexEnvf (GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_CONSTANT);
+ glTexEnvf (GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
+ glTexEnvf (GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);
+
+ (*w->screen->drawWindowGeometry) (w);
+
+ disableTexture (texture);
+
+ glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+
+ w->screen->activeTexture (GL_TEXTURE2_ARB);
+ }
+ else
+ {
+ (*w->screen->drawWindowGeometry) (w);
+ }
+
+ disableTexture (texture);
+
+ glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+
+ w->screen->activeTexture (GL_TEXTURE1_ARB);
+ }
+ else
+ {
+ glTexEnvf (GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE);
+ glTexEnvf (GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS);
+ glTexEnvf (GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_CONSTANT);
+ glTexEnvf (GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
+ glTexEnvf (GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);
+
+ constant[3] = attrib->opacity / 65535.0f;
+ constant[0] = constant[1] = constant[2] = constant[3] *
+ attrib->brightness / 65535.0f;
+
+ constant[0] = 0.5f + 0.5f * RED_SATURATION_WEIGHT * constant[0];
+ constant[1] = 0.5f + 0.5f * GREEN_SATURATION_WEIGHT * constant[1];
+ constant[2] = 0.5f + 0.5f * BLUE_SATURATION_WEIGHT * constant[2];
+
+ glTexEnvfv (GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, constant);
+
+ (*w->screen->drawWindowGeometry) (w);
+ }
+
+ disableTexture (texture);
+
+ glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+
+ w->screen->activeTexture (GL_TEXTURE0_ARB);
+
+ disableTexture (texture);
+
+ glColor4usv (defaultColor);
+ glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+
+ if (mask & PAINT_WINDOW_TRANSLUCENT_MASK)
+ glDisable (GL_BLEND);
+ }
+ else
+ {
+ enableTexture (w->screen, texture, filter);
+
+ if (mask & PAINT_WINDOW_TRANSLUCENT_MASK)
+ {
+ glEnable (GL_BLEND);
+ if (attrib->opacity != OPAQUE || attrib->brightness != BRIGHT)
+ {
+ GLushort color;
+
+ color = (attrib->opacity * attrib->brightness) >> 16;
+
+ glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+ glColor4us (color, color, color, attrib->opacity);
+
+ (*w->screen->drawWindowGeometry) (w);
+
+ glColor4usv (defaultColor);
+ glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+ }
+ else
+ {
+ (*w->screen->drawWindowGeometry) (w);
+ }
+
+ glDisable (GL_BLEND);
+ }
+ else if (attrib->brightness != BRIGHT)
+ {
+ glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+ glColor4us (attrib->brightness, attrib->brightness,
+ attrib->brightness, BRIGHT);
+
+ (*w->screen->drawWindowGeometry) (w);
+
+ glColor4usv (defaultColor);
+ glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+ }
+ else
+ {
+ (*w->screen->drawWindowGeometry) (w);
+ }
+
+ disableTexture (texture);
+ }
+
+ glPopMatrix ();
+}
+
+Bool
+paintWindow (CompWindow *w,
+ const WindowPaintAttrib *attrib,
+ Region region,
+ unsigned int mask)
+{
+ if (mask & PAINT_WINDOW_SOLID_MASK)
+ {
+ if (w->alpha)
+ return FALSE;
+
+ if (attrib->opacity != OPAQUE)
+ return FALSE;
+ }
+ else if (mask & PAINT_WINDOW_TRANSLUCENT_MASK)
+ {
+ if (!w->alpha && attrib->opacity == OPAQUE)
+ return FALSE;
+ }
+ else
+ {
+ if (w->alpha || attrib->opacity != OPAQUE)
+ mask |= PAINT_WINDOW_TRANSLUCENT_MASK;
+ else
+ mask |= PAINT_WINDOW_SOLID_MASK;
+ }
+
+ if (!w->texture.pixmap)
+ bindWindow (w);
+
+ if (mask & PAINT_WINDOW_TRANSFORMED_MASK)
+ region = &infiniteRegion;
+
+ w->vCount = 0;
+ (*w->screen->addWindowGeometry) (w, &w->matrix, 1, w->region, region);
+ if (w->vCount)
+ drawWindowTexture (w, &w->texture, attrib, mask);
+
+ return TRUE;
+}
+
+void
+paintBackground (CompScreen *s,
+ Region region,
+ unsigned int mask)
+{
+ CompTexture *bg = &s->backgroundTexture;
+ BoxPtr pBox = region->rects;
+ int n, nBox = region->numRects;
+ GLfloat *d, *data;
+
+ if (!nBox)
+ return;
+
+ if (s->desktopWindowCount)
+ {
+ if (bg->name)
+ {
+ finiTexture (s, bg);
+ initTexture (s, bg);
+ }
+
+ if (!(mask & PAINT_BACKGROUND_WITH_STENCIL_MASK))
+ return;
+ }
+ else
+ {
+ if (!bg->name)
+ updateScreenBackground (s, bg);
+ }
+
+ data = malloc (sizeof (GLfloat) * nBox * 16);
+ if (!data)
+ return;
+
+ d = data;
+ n = nBox;
+ while (n--)
+ {
+ *d++ = COMP_TEX_COORD_X (&bg->matrix, pBox->x1);
+ *d++ = COMP_TEX_COORD_Y (&bg->matrix, pBox->y2);
+
+ *d++ = pBox->x1;
+ *d++ = pBox->y2;
+
+ *d++ = COMP_TEX_COORD_X (&bg->matrix, pBox->x2);
+ *d++ = COMP_TEX_COORD_Y (&bg->matrix, pBox->y2);
+
+ *d++ = pBox->x2;
+ *d++ = pBox->y2;
+
+ *d++ = COMP_TEX_COORD_X (&bg->matrix, pBox->x2);
+ *d++ = COMP_TEX_COORD_Y (&bg->matrix, pBox->y1);
+
+ *d++ = pBox->x2;
+ *d++ = pBox->y1;
+
+ *d++ = COMP_TEX_COORD_X (&bg->matrix, pBox->x1);
+ *d++ = COMP_TEX_COORD_Y (&bg->matrix, pBox->y1);
+
+ *d++ = pBox->x1;
+ *d++ = pBox->y1;
+
+ pBox++;
+ }
+
+ if (mask & PAINT_BACKGROUND_WITH_STENCIL_MASK)
+ {
+ glEnable (GL_STENCIL_TEST);
+ glStencilFunc (GL_ALWAYS, s->stencilRef, ~0);
+ glStencilOp (GL_REPLACE, GL_REPLACE, GL_REPLACE);
+ }
+
+ glTexCoordPointer (2, GL_FLOAT, sizeof (GLfloat) * 4, data);
+ glVertexPointer (2, GL_FLOAT, sizeof (GLfloat) * 4, data + 2);
+
+ if (s->desktopWindowCount)
+ {
+ glDrawArrays (GL_QUADS, 0, nBox * 4);
+ }
+ else
+ {
+ if (mask & PAINT_BACKGROUND_ON_TRANSFORMED_SCREEN_MASK)
+ enableTexture (s, bg, COMP_TEXTURE_FILTER_GOOD);
+ else
+ enableTexture (s, bg, COMP_TEXTURE_FILTER_FAST);
+
+ glDrawArrays (GL_QUADS, 0, nBox * 4);
+
+ disableTexture (bg);
+ }
+
+ if (mask & PAINT_BACKGROUND_WITH_STENCIL_MASK)
+ {
+ glStencilFunc (GL_EQUAL, s->stencilRef, ~0);
+ glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP);
+ glDisable (GL_STENCIL_TEST);
+ }
+
+ free (data);
+}
diff --git a/src/plugin.c b/src/plugin.c
new file mode 100644
index 00000000..fb9ddfc3
--- /dev/null
+++ b/src/plugin.c
@@ -0,0 +1,404 @@
+/*
+ * 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 <dlfcn.h>
+
+#include <compiz.h>
+
+#define HOME_PLUGINDIR ".compiz/plugins"
+
+CompPlugin *plugins = 0;
+
+static Bool
+initPlugin (CompPlugin *p)
+{
+ CompDisplay *d = compDisplays;
+ int failed = 0;
+
+ if (!(*p->vTable->init) (p))
+ {
+ fprintf (stderr, "%s: InitPlugin '%s' failed\n", programName,
+ p->vTable->name);
+ return FALSE;
+ }
+
+ if (d)
+ {
+ if ((*d->initPluginForDisplay) (p, d))
+ {
+ CompScreen *s, *failedScreen = d->screens;
+
+ for (s = d->screens; s; s = s->next)
+ {
+ if (!p->vTable->initScreen || (*s->initPluginForScreen) (p, s))
+ {
+ CompWindow *w, *failedWindow = s->windows;
+
+ for (w = s->windows; w; w = w->next)
+ {
+ if (p->vTable->initWindow &&
+ !(*p->vTable->initWindow) (p, w))
+ {
+ fprintf (stderr, "%s: Plugin '%s':initWindow "
+ "failed\n", programName, p->vTable->name);
+ failedWindow = w;
+ failed = 1;
+ break;
+ }
+ }
+
+ for (w = s->windows; w != failedWindow; w = w->next)
+ {
+ if (p->vTable->finiWindow)
+ (*p->vTable->finiWindow) (p, w);
+ }
+ }
+ else
+ {
+ fprintf (stderr, "%s: Plugin '%s':initScreen failed\n",
+ programName, p->vTable->name);
+ failedScreen = s;
+ failed = 1;
+ break;
+ }
+ }
+
+ for (s = d->screens; s != failedScreen; s = s->next)
+ (*s->finiPluginForScreen) (p, s);
+ }
+ else
+ {
+ fprintf (stderr, "%s: Plugin '%s':initDisplay failed\n",
+ programName, p->vTable->name);
+
+ failed = 1;
+ (*d->finiPluginForDisplay) (p, d);
+ }
+ }
+
+ if (failed)
+ {
+ (*p->vTable->fini) (p);
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+finiPlugin (CompPlugin *p)
+{
+ CompDisplay *d = compDisplays;
+ CompScreen *s;
+
+ if (d)
+ {
+ for (s = d->screens; s; s = s->next)
+ {
+ CompWindow *w = s->windows;
+
+ if (p->vTable->finiWindow)
+ {
+ for (w = s->windows; w; w = w->next)
+ (*p->vTable->finiWindow) (p, w);
+ }
+
+ (*s->finiPluginForScreen) (p, s);
+ }
+
+ (*d->finiPluginForDisplay) (p, d);
+ }
+
+ (*p->vTable->fini) (p);
+}
+
+void
+screenInitPlugins (CompScreen *s)
+{
+ CompPlugin *p;
+ int i, j = 0;
+
+ for (p = plugins; p; p = p->next)
+ j++;
+
+ while (j--)
+ {
+ i = 0;
+ for (p = plugins; i < j; p = p->next)
+ i++;
+
+ if (p->vTable->initScreen)
+ (*s->initPluginForScreen) (p, s);
+ }
+}
+
+void
+screenFiniPlugins (CompScreen *s)
+{
+ CompPlugin *p;
+
+ for (p = plugins; p; p = p->next)
+ {
+ if (p->vTable->finiScreen)
+ (*s->initPluginForScreen) (p, s);
+ }
+}
+
+void
+windowInitPlugins (CompWindow *w)
+{
+ CompPlugin *p;
+
+ for (p = plugins; p; p = p->next)
+ {
+ if (p->vTable->initWindow)
+ (*p->vTable->initWindow) (p, w);
+ }
+}
+
+void
+windowFiniPlugins (CompWindow *w)
+{
+ CompPlugin *p;
+
+ for (p = plugins; p; p = p->next)
+ {
+ if (p->vTable->finiWindow)
+ (*p->vTable->finiWindow) (p, w);
+ }
+}
+
+CompPlugin *
+findActivePlugin (char *name)
+{
+ CompPlugin *p;
+
+ for (p = plugins; p; p = p->next)
+ {
+ if (strcmp (p->vTable->name, name) == 0)
+ return p;
+ }
+
+ return 0;
+}
+
+CompPlugin *
+loadPlugin (char *name)
+{
+ CompPlugin *p;
+ char *file, *home, *plugindir;
+
+ p = malloc (sizeof (CompPlugin));
+ if (!p)
+ return 0;
+
+ file = malloc (strlen (name) + 7);
+ if (!file)
+ {
+ free (p);
+ return 0;
+ }
+
+ sprintf (file, "lib%s.so", name);
+
+ p->next = 0;
+ p->dlhand = 0;
+ p->vTable = 0;
+
+ home = getenv ("HOME");
+ if (home)
+ {
+ plugindir = malloc (strlen (home) +
+ strlen (HOME_PLUGINDIR) +
+ strlen (file) + 3);
+ if (plugindir)
+ {
+ sprintf (plugindir, "%s/%s/%s", home, HOME_PLUGINDIR, file);
+ p->dlhand = dlopen (plugindir, RTLD_LAZY);
+ free (plugindir);
+ }
+ }
+
+ if (!p->dlhand)
+ {
+ plugindir = malloc (strlen (PLUGINDIR) + strlen (file) + 2);
+ if (plugindir)
+ {
+ sprintf (plugindir, "%s/%s", PLUGINDIR, file);
+ p->dlhand = dlopen (plugindir, RTLD_LAZY);
+ free (plugindir);
+ }
+
+ if (!p->dlhand)
+ p->dlhand = dlopen (file, RTLD_LAZY);
+ }
+
+ if (p->dlhand)
+ {
+ PluginGetInfoProc getInfo;
+ char *error;
+
+ dlerror ();
+
+ getInfo = (PluginGetInfoProc) dlsym (p->dlhand, "getCompPluginInfo");
+
+ error = dlerror ();
+ if (error)
+ {
+ fprintf (stderr, "%s: dlsym: %s\n", programName, error);
+
+ getInfo = 0;
+ }
+
+ if (getInfo)
+ {
+ p->vTable = (*getInfo) ();
+ if (!p->vTable)
+ {
+ fprintf (stderr, "%s: Couldn't get vtable from '%s' plugin\n",
+ programName, file);
+
+ dlclose (p->dlhand);
+ free (p);
+ p = 0;
+ }
+ }
+ else
+ {
+ fprintf (stderr, "%s: Failed to lookup getCompPluginInfo in '%s' "
+ "plugin\n", programName, file);
+
+ dlclose (p->dlhand);
+ free (p);
+ p = 0;
+ }
+ }
+ else
+ {
+ fprintf (stderr, "%s: Couldn't load plugin '%s'\n", programName,
+ file);
+ free (p);
+ p = 0;
+ }
+
+ free (file);
+
+ return p;
+}
+
+void
+unloadPlugin (CompPlugin *p)
+{
+ dlclose (p->dlhand);
+ free (p);
+}
+
+static Bool
+checkPluginDeps (CompPlugin *p)
+{
+ CompPluginDep *deps = p->vTable->deps;
+ int nDeps = p->vTable->nDeps;
+
+ while (nDeps--)
+ {
+ switch (deps->rule) {
+ case CompPluginRuleBefore:
+ if (findActivePlugin (deps->plugin))
+ {
+ fprintf (stderr, "%s: '%s' plugin must be loaded before '%s' "
+ "plugin\n", programName, p->vTable->name, deps->plugin);
+
+ return FALSE;
+ }
+ break;
+ case CompPluginRuleAfter:
+ if (!findActivePlugin (deps->plugin))
+ {
+ fprintf (stderr, "%s: '%s' plugin must be loaded after '%s' "
+ "plugin\n", programName, p->vTable->name, deps->plugin);
+
+ return FALSE;
+ }
+ break;
+ }
+
+ deps++;
+ }
+
+ return TRUE;
+}
+
+Bool
+pushPlugin (CompPlugin *p)
+{
+ if (findActivePlugin (p->vTable->name))
+ {
+ fprintf (stderr, "%s: Plugin '%s' already active\n", programName,
+ p->vTable->name);
+
+ return FALSE;
+ }
+
+ if (!checkPluginDeps (p))
+ {
+ fprintf (stderr, "%s: Can't activate '%s' plugin due to dependency "
+ "problems\n", programName, p->vTable->name);
+
+ return FALSE;
+ }
+
+ p->next = plugins;
+ plugins = p;
+
+ if (!initPlugin (p))
+ {
+ fprintf (stderr, "%s: Couldn't activate plugin '%s'\n", programName,
+ p->vTable->name);
+ plugins = p->next;
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+CompPlugin *
+popPlugin (void)
+{
+ CompPlugin *p = plugins;
+
+ if (!p)
+ return 0;
+
+ finiPlugin (p);
+
+ plugins = p->next;
+
+ return p;
+}
diff --git a/src/privates.c b/src/privates.c
new file mode 100644
index 00000000..ea5b57ac
--- /dev/null
+++ b/src/privates.c
@@ -0,0 +1,68 @@
+/*
+ * 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 <compiz.h>
+
+int
+allocatePrivateIndex (int *len,
+ char **indices,
+ ReallocPrivatesProc reallocProc,
+ void *closure)
+{
+ char *newIndices;
+ int i;
+
+ for (i = 0; i < *len; i++)
+ {
+ if (!(*indices)[i])
+ {
+ (*indices)[i] = 1;
+ return i;
+ }
+ }
+
+ newIndices = (char *) realloc (*indices, (*len + 1) * sizeof (char));
+ if (!newIndices)
+ return -1;
+
+ newIndices[*len] = 1;
+ *indices = newIndices;
+
+ if (!(*reallocProc) (*len + 1, closure))
+ return -1;
+
+ return (*len)++;
+}
+
+void
+freePrivateIndex (int len,
+ char *indices,
+ int index)
+{
+ if (index < len)
+ indices[index] = 0;
+}
diff --git a/src/readpng.c b/src/readpng.c
new file mode 100644
index 00000000..79bb5a53
--- /dev/null
+++ b/src/readpng.c
@@ -0,0 +1,266 @@
+/*
+ * 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 <png.h>
+#include <setjmp.h>
+
+#include <compiz.h>
+
+#define HOME_IMAGEDIR ".compiz/images"
+
+#define PNG_SIG_SIZE 8
+
+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,
+ char **data,
+ unsigned int *width,
+ unsigned int *height)
+{
+ png_uint_32 png_width, png_height;
+ int depth, color_type, interlace, i;
+ unsigned int pixel_size;
+ png_byte **row_pointers;
+
+ png_read_info (png, info);
+
+ png_get_IHDR (png, info,
+ &png_width, &png_height, &depth,
+ &color_type, &interlace, NULL, NULL);
+
+ *width = png_width;
+ *height = 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;
+ *data = (char *) malloc (png_width * png_height * pixel_size);
+ if (*data == NULL)
+ return FALSE;
+
+ row_pointers = (png_byte **) malloc (png_height * sizeof (char *));
+ if (!row_pointers)
+ {
+ free (*data);
+ return FALSE;
+ }
+
+ for (i = 0; i < png_height; i++)
+ row_pointers[i] = (png_byte *) (*data + i * png_width * pixel_size);
+
+ png_read_image (png, row_pointers);
+ png_read_end (png, info);
+
+ free (row_pointers);
+
+ return TRUE;
+}
+
+Bool
+readPng (const char *filename,
+ char **data,
+ unsigned int *width,
+ unsigned int *height)
+{
+ unsigned char png_sig[PNG_SIG_SIZE];
+ FILE *file;
+ int sig_bytes;
+ png_struct *png;
+ png_info *info;
+ Bool status;
+
+ file = fopen (filename, "r");
+ if (!file)
+ {
+ char *home, *imagedir;
+
+ home = getenv ("HOME");
+ if (home)
+ {
+ imagedir = malloc (strlen (home) +
+ strlen (HOME_IMAGEDIR) +
+ strlen (filename) + 3);
+ if (imagedir)
+ {
+ sprintf (imagedir, "%s/%s/%s", home, HOME_IMAGEDIR, filename);
+ file = fopen (imagedir, "r");
+ free (imagedir);
+ }
+ }
+
+ if (!file)
+ {
+ imagedir = malloc (strlen (IMAGEDIR) + strlen (filename) + 2);
+ if (imagedir)
+ {
+ sprintf (imagedir, "%s/%s", IMAGEDIR, filename);
+ file = fopen (imagedir, "r");
+ free (imagedir);
+ }
+
+ if (!file)
+ return FALSE;
+ }
+ }
+
+ sig_bytes = fread (png_sig, 1, PNG_SIG_SIZE, file);
+ if (png_check_sig (png_sig, sig_bytes) == 0)
+ {
+ fclose (file);
+ 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 == NULL)
+ {
+ png_destroy_read_struct (&png, NULL, NULL);
+ fclose (file);
+
+ 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);
+ fclose (file);
+
+ return status;
+}
+
+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;
+}
+
+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 == NULL)
+ {
+ 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;
+}
+
diff --git a/src/screen.c b/src/screen.c
new file mode 100644
index 00000000..27c7d9d8
--- /dev/null
+++ b/src/screen.c
@@ -0,0 +1,2521 @@
+/*
+ * 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 <math.h>
+#include <dlfcn.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/extensions/Xrandr.h>
+#include <X11/cursorfont.h>
+
+#include <compiz.h>
+
+#define DETECT_REFRESH_RATE_DEFAULT TRUE
+
+#define SCREEN_SIZE_DEFAULT 4
+#define SCREEN_SIZE_MIN 4
+#define SCREEN_SIZE_MAX 32
+
+#define CLOSE_WINDOW_KEY_DEFAULT "F4"
+#define CLOSE_WINDOW_MODIFIERS_DEFAULT (CompPressMask | CompAltMask)
+
+#define MAIN_MENU_KEY_DEFAULT "F1"
+#define MAIN_MENU_MODIFIERS_DEFAULT (CompPressMask | CompAltMask)
+
+#define RUN_DIALOG_KEY_DEFAULT "F2"
+#define RUN_DIALOG_MODIFIERS_DEFAULT (CompPressMask | CompAltMask)
+
+#define NUM_OPTIONS(s) (sizeof ((s)->opt) / sizeof (CompOption))
+
+static int
+reallocScreenPrivate (int size,
+ void *closure)
+{
+ CompDisplay *d = (CompDisplay *) closure;
+ CompScreen *s;
+ void *privates;
+
+ for (s = d->screens; s; s = s->next)
+ {
+ privates = realloc (s->privates, size * sizeof (CompPrivate));
+ if (!privates)
+ return FALSE;
+
+ s->privates = (CompPrivate *) privates;
+ }
+
+ return TRUE;
+}
+
+int
+allocateScreenPrivateIndex (CompDisplay *display)
+{
+ return allocatePrivateIndex (&display->screenPrivateLen,
+ &display->screenPrivateIndices,
+ reallocScreenPrivate,
+ (void *) display);
+}
+
+void
+freeScreenPrivateIndex (CompDisplay *display,
+ int index)
+{
+ freePrivateIndex (display->screenPrivateLen,
+ display->screenPrivateIndices,
+ index);
+}
+
+static void
+setVirtualScreenSize (CompScreen *screen,
+ int size)
+{
+ unsigned long data[2];
+
+ data[0] = screen->width * size;
+ data[1] = screen->height;
+
+ XChangeProperty (screen->display->display, screen->root,
+ screen->display->desktopGeometryAtom,
+ XA_CARDINAL, 32, PropModeReplace,
+ (unsigned char *) data, 2);
+
+ screen->size = size;
+}
+
+CompOption *
+compGetScreenOptions (CompScreen *screen,
+ int *count)
+{
+ *count = NUM_OPTIONS (screen);
+ return screen->opt;
+}
+
+static Bool
+setScreenOption (CompScreen *screen,
+ char *name,
+ CompOptionValue *value)
+{
+ CompOption *o;
+ int index;
+
+ o = compFindOption (screen->opt, NUM_OPTIONS (screen), name, &index);
+ if (!o)
+ return FALSE;
+
+ switch (index) {
+ case COMP_SCREEN_OPTION_DETECT_REFRESH_RATE:
+ if (compSetBoolOption (o, value))
+ return TRUE;
+ break;
+ case COMP_SCREEN_OPTION_REFRESH_RATE:
+ if (screen->opt[COMP_SCREEN_OPTION_DETECT_REFRESH_RATE].value.b)
+ return FALSE;
+
+ if (compSetIntOption (o, value))
+ {
+ screen->redrawTime = 1000 / o->value.i;
+ screen->optimalRedrawTime = screen->redrawTime;
+ return TRUE;
+ }
+ break;
+ case COMP_SCREEN_OPTION_SIZE:
+ if (compSetIntOption (o, value))
+ {
+ if (o->value.i * screen->width > MAXSHORT)
+ return FALSE;
+
+ setVirtualScreenSize (screen, o->value.i);
+ return TRUE;
+ }
+ break;
+ case COMP_SCREEN_OPTION_COMMAND0:
+ case COMP_SCREEN_OPTION_COMMAND1:
+ if (compSetStringOption (o, value))
+ return TRUE;
+ break;
+ case COMP_SCREEN_OPTION_CLOSE_WINDOW:
+ case COMP_SCREEN_OPTION_MAIN_MENU:
+ case COMP_SCREEN_OPTION_RUN_DIALOG:
+ case COMP_SCREEN_OPTION_RUN_COMMAND0:
+ case COMP_SCREEN_OPTION_RUN_COMMAND1:
+ if (addScreenBinding (screen, &value->bind))
+ {
+ removeScreenBinding (screen, &o->value.bind);
+
+ if (compSetBindingOption (o, value))
+ return TRUE;
+ }
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+static Bool
+setScreenOptionForPlugin (CompScreen *screen,
+ char *plugin,
+ char *name,
+ CompOptionValue *value)
+{
+ CompPlugin *p;
+
+ p = findActivePlugin (plugin);
+ if (p && p->vTable->setScreenOption)
+ return (*p->vTable->setScreenOption) (screen, name, value);
+
+ return FALSE;
+}
+
+static void
+compScreenInitOptions (CompScreen *screen)
+{
+ CompOption *o;
+
+ o = &screen->opt[COMP_SCREEN_OPTION_DETECT_REFRESH_RATE];
+ o->name = "detect_refresh_rate";
+ o->shortDesc = "Detect Refresh Rate";
+ o->longDesc = "Automatic detection of refresh rate";
+ o->type = CompOptionTypeBool;
+ o->value.b = DETECT_REFRESH_RATE_DEFAULT;
+
+ o = &screen->opt[COMP_SCREEN_OPTION_REFRESH_RATE];
+ o->name = "refresh_rate";
+ o->shortDesc = "Refresh Rate";
+ o->longDesc = "The rate at which the screen is redrawn (times/second)";
+ o->type = CompOptionTypeInt;
+ o->value.i = defaultRefreshRate;
+ o->rest.i.min = 1;
+ o->rest.i.max = 200;
+
+ o = &screen->opt[COMP_SCREEN_OPTION_SIZE];
+ o->name = "size";
+ o->shortDesc = "Virtual Size";
+ o->longDesc = "Screen size multiplier for virtual size";
+ o->type = CompOptionTypeInt;
+ o->value.i = SCREEN_SIZE_DEFAULT;
+ o->rest.i.min = SCREEN_SIZE_MIN;
+ o->rest.i.max = SCREEN_SIZE_MAX;
+
+ o = &screen->opt[COMP_SCREEN_OPTION_CLOSE_WINDOW];
+ o->name = "close_window";
+ o->shortDesc = "Close Window";
+ o->longDesc = "Close active window";
+ o->type = CompOptionTypeBinding;
+ o->value.bind.type = CompBindingTypeKey;
+ o->value.bind.u.key.modifiers = CLOSE_WINDOW_MODIFIERS_DEFAULT;
+ o->value.bind.u.key.keycode =
+ XKeysymToKeycode (screen->display->display,
+ XStringToKeysym (CLOSE_WINDOW_KEY_DEFAULT));
+
+ o = &screen->opt[COMP_SCREEN_OPTION_MAIN_MENU];
+ o->name = "main_menu";
+ o->shortDesc = "Main Menu";
+ o->longDesc = "Open main menu";
+ o->type = CompOptionTypeBinding;
+ o->value.bind.type = CompBindingTypeKey;
+ o->value.bind.u.key.modifiers = MAIN_MENU_MODIFIERS_DEFAULT;
+ o->value.bind.u.key.keycode =
+ XKeysymToKeycode (screen->display->display,
+ XStringToKeysym (MAIN_MENU_KEY_DEFAULT));
+
+ o = &screen->opt[COMP_SCREEN_OPTION_RUN_DIALOG];
+ o->name = "run";
+ o->shortDesc = "Run";
+ o->longDesc = "Run application";
+ o->type = CompOptionTypeBinding;
+ o->value.bind.type = CompBindingTypeKey;
+ o->value.bind.u.key.modifiers = RUN_DIALOG_MODIFIERS_DEFAULT;
+ o->value.bind.u.key.keycode =
+ XKeysymToKeycode (screen->display->display,
+ XStringToKeysym (RUN_DIALOG_KEY_DEFAULT));
+
+ o = &screen->opt[COMP_SCREEN_OPTION_COMMAND0];
+ o->name = "command0";
+ o->shortDesc = "Command line";
+ o->longDesc = "Command line to be executed in shell";
+ o->type = CompOptionTypeString;
+ o->value.s = strdup ("");
+ o->rest.s.string = NULL;
+ o->rest.s.nString = 0;
+
+ o = &screen->opt[COMP_SCREEN_OPTION_RUN_COMMAND0];
+ o->name = "run_command0";
+ o->shortDesc = "Run command";
+ o->longDesc = "Run shell command";
+ o->type = CompOptionTypeBinding;
+ o->value.bind.type = CompBindingTypeKey;
+ o->value.bind.u.key.modifiers = 0;
+ o->value.bind.u.key.keycode = 0;
+
+ o = &screen->opt[COMP_SCREEN_OPTION_COMMAND1];
+ o->name = "command1";
+ o->shortDesc = "Command line";
+ o->longDesc = "Command line to be executed in shell";
+ o->type = CompOptionTypeString;
+ o->value.s = strdup ("");
+ o->rest.s.string = NULL;
+ o->rest.s.nString = 0;
+
+ o = &screen->opt[COMP_SCREEN_OPTION_RUN_COMMAND1];
+ o->name = "run_command1";
+ o->shortDesc = "Run command";
+ o->longDesc = "Run shell command";
+ o->type = CompOptionTypeBinding;
+ o->value.bind.type = CompBindingTypeKey;
+ o->value.bind.u.key.modifiers = 0;
+ o->value.bind.u.key.keycode = 0;
+}
+
+static Bool
+initPluginForScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ if (p->vTable->initScreen)
+ return (*p->vTable->initScreen) (p, s);
+
+ return FALSE;
+}
+
+static void
+finiPluginForScreen (CompPlugin *p,
+ CompScreen *s)
+{
+ if (p->vTable->finiScreen)
+ (*p->vTable->finiScreen) (p, s);
+}
+
+static void
+updateStartupFeedback (CompScreen *s)
+{
+ if (s->startupSequences)
+ XDefineCursor (s->display->display, s->root, s->busyCursor);
+ else
+ XDefineCursor (s->display->display, s->root, s->normalCursor);
+}
+
+#define STARTUP_TIMEOUT_DELAY 15000
+
+static Bool
+startupSequenceTimeout (void *data)
+{
+ CompScreen *screen = data;
+ CompStartupSequence *s;
+ struct timeval now, active;
+ double elapsed;
+
+ gettimeofday (&now, NULL);
+
+ for (s = screen->startupSequences; s; s = s->next)
+ {
+ sn_startup_sequence_get_last_active_time (s->sequence,
+ &active.tv_sec,
+ &active.tv_usec);
+
+ elapsed = ((((double) now.tv_sec - active.tv_sec) * 1000000.0 +
+ (now.tv_usec - active.tv_usec))) / 1000.0;
+
+ if (elapsed > STARTUP_TIMEOUT_DELAY)
+ sn_startup_sequence_complete (s->sequence);
+ }
+
+ return TRUE;
+}
+
+static void
+addSequence (CompScreen *screen,
+ SnStartupSequence *sequence)
+{
+ CompStartupSequence *s;
+
+ s = malloc (sizeof (CompStartupSequence));
+ if (!s)
+ return;
+
+ sn_startup_sequence_ref (sequence);
+
+ s->next = screen->startupSequences;
+ s->sequence = sequence;
+
+ screen->startupSequences = s;
+
+ if (!screen->startupSequenceTimeoutHandle)
+ compAddTimeout (1000,
+ startupSequenceTimeout,
+ screen);
+
+ updateStartupFeedback (screen);
+}
+
+static void
+removeSequence (CompScreen *screen,
+ SnStartupSequence *sequence)
+{
+ CompStartupSequence *s, *p = NULL;
+
+ for (s = screen->startupSequences; s; s = s->next)
+ {
+ if (s->sequence == sequence)
+ break;
+
+ p = s;
+ }
+
+ if (!s)
+ return;
+
+ sn_startup_sequence_unref (sequence);
+
+ if (p)
+ p->next = s->next;
+ else
+ screen->startupSequences = NULL;
+
+ free (s);
+
+ if (!screen->startupSequences && screen->startupSequenceTimeoutHandle)
+ {
+ compRemoveTimeout (screen->startupSequenceTimeoutHandle);
+ screen->startupSequenceTimeoutHandle = 0;
+ }
+
+ updateStartupFeedback (screen);
+}
+
+static void
+compScreenSnEvent (SnMonitorEvent *event,
+ void *userData)
+{
+ CompScreen *screen = userData;
+ SnStartupSequence *sequence;
+
+ sequence = sn_monitor_event_get_startup_sequence (event);
+
+ switch (sn_monitor_event_get_type (event)) {
+ case SN_MONITOR_EVENT_INITIATED:
+ addSequence (screen, sequence);
+ break;
+ case SN_MONITOR_EVENT_COMPLETED:
+ removeSequence (screen, sn_monitor_event_get_startup_sequence (event));
+ break;
+ case SN_MONITOR_EVENT_CHANGED:
+ case SN_MONITOR_EVENT_CANCELED:
+ break;
+ }
+}
+
+static void
+frustum (GLfloat left,
+ GLfloat right,
+ GLfloat bottom,
+ GLfloat top,
+ GLfloat nearval,
+ GLfloat farval)
+{
+ GLfloat x, y, a, b, c, d;
+ GLfloat m[16];
+
+ x = (2.0 * nearval) / (right - left);
+ y = (2.0 * nearval) / (top - bottom);
+ a = (right + left) / (right - left);
+ b = (top + bottom) / (top - bottom);
+ c = -(farval + nearval) / ( farval - nearval);
+ d = -(2.0 * farval * nearval) / (farval - nearval);
+
+#define M(row,col) m[col*4+row]
+ M(0,0) = x; M(0,1) = 0.0F; M(0,2) = a; M(0,3) = 0.0F;
+ M(1,0) = 0.0F; M(1,1) = y; M(1,2) = b; M(1,3) = 0.0F;
+ M(2,0) = 0.0F; M(2,1) = 0.0F; M(2,2) = c; M(2,3) = d;
+ M(3,0) = 0.0F; M(3,1) = 0.0F; M(3,2) = -1.0F; M(3,3) = 0.0F;
+#undef M
+
+ glMultMatrixf (m);
+}
+
+static void
+perspective (GLfloat fovy,
+ GLfloat aspect,
+ GLfloat zNear,
+ GLfloat zFar)
+{
+ GLfloat xmin, xmax, ymin, ymax;
+
+ ymax = zNear * tan (fovy * M_PI / 360.0);
+ ymin = -ymax;
+ xmin = ymin * aspect;
+ xmax = ymax * aspect;
+
+ frustum (xmin, xmax, ymin, ymax, zNear, zFar);
+}
+
+static void
+reshape (CompScreen *s,
+ int w,
+ int h)
+{
+ s->width = w;
+ s->height = h;
+
+ glMatrixMode (GL_PROJECTION);
+ glLoadIdentity ();
+ glMatrixMode (GL_MODELVIEW);
+ glLoadIdentity ();
+ glDepthRange (0, 1);
+ glViewport (-1, -1, 2, 2);
+ glRasterPos2f (0, 0);
+
+ s->rasterX = s->rasterY = 0;
+
+ glViewport (0, 0, w, h);
+ glMatrixMode (GL_PROJECTION);
+ glLoadIdentity ();
+ perspective (60.0f, 1.0f, 0.1f, 100.0f);
+ glMatrixMode (GL_MODELVIEW);
+
+ s->region.rects = &s->region.extents;
+ s->region.numRects = 1;
+ s->region.extents.x1 = 0;
+ s->region.extents.y1 = 0;
+ s->region.extents.x2 = w;
+ s->region.extents.y2 = h;
+ s->region.size = 1;
+
+ updateWorkareaForScreen (s);
+}
+
+void
+configureScreen (CompScreen *s,
+ XConfigureEvent *ce)
+{
+ if (s->attrib.width != ce->width ||
+ s->attrib.height != ce->height)
+ {
+ s->attrib.width = ce->width;
+ s->attrib.height = ce->height;
+
+ reshape (s, ce->width, ce->height);
+
+ damageScreen (s);
+ }
+}
+
+static FuncPtr
+getProcAddress (CompScreen *s,
+ const char *name)
+{
+ static void *dlhand = NULL;
+ FuncPtr funcPtr = NULL;
+
+ if (s->getProcAddress)
+ funcPtr = s->getProcAddress ((GLubyte *) name);
+
+ if (!funcPtr)
+ {
+ if (!dlhand)
+ dlhand = dlopen (NULL, RTLD_LAZY);
+
+ if (dlhand)
+ {
+ dlerror ();
+ funcPtr = (FuncPtr) dlsym (dlhand, name);
+ if (dlerror () != NULL)
+ funcPtr = NULL;
+ }
+ }
+
+ return funcPtr;
+}
+
+void
+updateScreenBackground (CompScreen *screen,
+ CompTexture *texture)
+{
+ Display *dpy = screen->display->display;
+ Atom pixmapAtom, actualType;
+ int actualFormat, i, status;
+ unsigned int width = 1, height = 1, depth = 0;
+ unsigned long nItems;
+ unsigned long bytesAfter;
+ unsigned char *prop;
+ Pixmap pixmap = 0;
+
+ pixmapAtom = XInternAtom (dpy, "PIXMAP", FALSE);
+
+ for (i = 0; pixmap == 0 && i < 2; i++)
+ {
+ status = XGetWindowProperty (dpy, screen->root,
+ screen->display->xBackgroundAtom[i],
+ 0, 4, FALSE, AnyPropertyType,
+ &actualType, &actualFormat, &nItems,
+ &bytesAfter, &prop);
+
+ if (status == Success && nItems && prop)
+ {
+ if (actualType == pixmapAtom &&
+ actualFormat == 32 &&
+ nItems == 1)
+ {
+ Pixmap p;
+
+ memcpy (&p, prop, 4);
+
+ if (p)
+ {
+ unsigned int ui;
+ int i;
+ Window w;
+
+ if (XGetGeometry (dpy, p, &w, &i, &i,
+ &width, &height, &ui, &depth))
+ {
+ if (depth == screen->attrib.depth)
+ pixmap = p;
+ }
+ }
+ }
+
+ XFree (prop);
+ }
+ }
+
+ if (!testMode && pixmap)
+ {
+ if (pixmap == texture->pixmap)
+ return;
+
+ finiTexture (screen, texture);
+ initTexture (screen, texture);
+
+ if (!bindPixmapToTexture (screen, texture, pixmap,
+ width, height, depth))
+ {
+ fprintf (stderr, "%s: Couldn't bind background pixmap 0x%x to "
+ "texture\n", programName, (int) pixmap);
+ }
+ }
+ else
+ {
+ finiTexture (screen, texture);
+ initTexture (screen, texture);
+ }
+
+ if (!texture->name)
+ readImageToTexture (screen, texture, backgroundImage, &width, &height);
+
+ if (texture->target == GL_TEXTURE_2D)
+ {
+ glBindTexture (texture->target, texture->name);
+ glTexParameteri (texture->target, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri (texture->target, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ glBindTexture (texture->target, 0);
+ }
+}
+
+void
+detectRefreshRateOfScreen (CompScreen *s)
+{
+ if (s->opt[COMP_SCREEN_OPTION_DETECT_REFRESH_RATE].value.b)
+ {
+ XRRScreenConfiguration *config;
+ char *name;
+ CompOptionValue value;
+
+ config = XRRGetScreenInfo (s->display->display, s->root);
+ value.i = (int) XRRConfigCurrentRate (config);
+
+ XRRFreeScreenConfigInfo (config);
+
+ name = s->opt[COMP_SCREEN_OPTION_REFRESH_RATE].name;
+
+ s->opt[COMP_SCREEN_OPTION_DETECT_REFRESH_RATE].value.b = FALSE;
+ (*s->setScreenOption) (s, name, &value);
+ s->opt[COMP_SCREEN_OPTION_DETECT_REFRESH_RATE].value.b = TRUE;
+ }
+}
+
+static void
+setSupportingWmCheck (CompScreen *s)
+{
+ CompDisplay *d = s->display;
+
+ XChangeProperty (d->display, s->grabWindow, d->supportingWmCheckAtom,
+ XA_WINDOW, 32, PropModeReplace,
+ (unsigned char *) &s->grabWindow, 1);
+
+ XChangeProperty (d->display, s->grabWindow, d->wmNameAtom,
+ d->utf8StringAtom, 8, PropModeReplace,
+ (unsigned char *) PACKAGE, strlen (PACKAGE));
+ XChangeProperty (d->display, s->grabWindow, d->winStateAtom,
+ XA_ATOM, 32, PropModeReplace,
+ (unsigned char *) &d->winStateSkipTaskbarAtom, 1);
+ XChangeProperty (d->display, s->grabWindow, d->winStateAtom,
+ XA_ATOM, 32, PropModeAppend,
+ (unsigned char *) &d->winStateSkipPagerAtom, 1);
+ XChangeProperty (d->display, s->grabWindow, d->winStateAtom,
+ XA_ATOM, 32, PropModeAppend,
+ (unsigned char *) &d->winStateHiddenAtom, 1);
+
+ XChangeProperty (d->display, s->root, d->supportingWmCheckAtom,
+ XA_WINDOW, 32, PropModeReplace,
+ (unsigned char *) &s->grabWindow, 1);
+}
+
+static void
+setSupported (CompScreen *s)
+{
+ CompDisplay *d = s->display;
+ Atom data[256];
+ int i = 0;
+
+ data[i++] = d->supportedAtom;
+ data[i++] = d->supportingWmCheckAtom;
+
+ data[i++] = d->utf8StringAtom;
+
+ data[i++] = d->clientListAtom;
+ data[i++] = d->clientListStackingAtom;
+
+ data[i++] = d->winActiveAtom;
+
+ data[i++] = d->desktopViewportAtom;
+ data[i++] = d->desktopGeometryAtom;
+ data[i++] = d->currentDesktopAtom;
+ data[i++] = d->numberOfDesktopsAtom;
+ data[i++] = d->showingDesktopAtom;
+
+ data[i++] = d->workareaAtom;
+
+ data[i++] = d->wmNameAtom;
+/*
+ data[i++] = d->wmVisibleNameAtom;
+*/
+
+ data[i++] = d->wmStrutAtom;
+ data[i++] = d->wmStrutPartialAtom;
+
+/*
+ data[i++] = d->wmPidAtom;
+ data[i++] = d->wmUserTimeAtom;
+*/
+
+ data[i++] = d->frameExtentsAtom;
+ data[i++] = d->frameWindowAtom;
+
+ data[i++] = d->winStateModalAtom;
+ data[i++] = d->winStateStickyAtom;
+ data[i++] = d->winStateMaximizedVertAtom;
+ data[i++] = d->winStateMaximizedHorzAtom;
+ data[i++] = d->winStateShadedAtom;
+ data[i++] = d->winStateSkipTaskbarAtom;
+ data[i++] = d->winStateSkipPagerAtom;
+ data[i++] = d->winStateHiddenAtom;
+ data[i++] = d->winStateFullscreenAtom;
+ data[i++] = d->winStateAboveAtom;
+ data[i++] = d->winStateBelowAtom;
+ data[i++] = d->winStateDemandsAttentionAtom;
+
+ data[i++] = d->winOpacityAtom;
+ data[i++] = d->winBrightnessAtom;
+
+ if (s->canDoSaturated)
+ {
+ data[i++] = d->winSaturationAtom;
+ data[i++] = d->winStateDisplayModalAtom;
+ }
+
+ data[i++] = d->wmAllowedActionsAtom;
+
+ data[i++] = d->winActionMoveAtom;
+ data[i++] = d->winActionResizeAtom;
+ data[i++] = d->winActionStickAtom;
+ data[i++] = d->winActionMinimizeAtom;
+ data[i++] = d->winActionMaximizeHorzAtom;
+ data[i++] = d->winActionMaximizeVertAtom;
+ data[i++] = d->winActionFullscreenAtom;
+ data[i++] = d->winActionCloseAtom;
+
+ data[i++] = d->winTypeAtom;
+ data[i++] = d->winTypeDesktopAtom;
+ data[i++] = d->winTypeDockAtom;
+ data[i++] = d->winTypeToolbarAtom;
+ data[i++] = d->winTypeMenuAtom;
+ data[i++] = d->winTypeSplashAtom;
+ data[i++] = d->winTypeDialogAtom;
+ data[i++] = d->winTypeUtilAtom;
+ data[i++] = d->winTypeNormalAtom;
+
+ data[i++] = d->wmDeleteWindowAtom;
+ data[i++] = d->wmPingAtom;
+
+ data[i++] = d->wmMoveResizeAtom;
+ data[i++] = d->moveResizeWindowAtom;
+
+ XChangeProperty (d->display, s->root, d->supportedAtom, XA_ATOM, 32,
+ PropModeReplace, (unsigned char *) data, i);
+}
+
+static void
+setDesktopHints (CompScreen *s)
+{
+ CompDisplay *d = s->display;
+ unsigned long data[2];
+ Atom actual;
+ int result, format;
+ unsigned long n, left;
+ unsigned char *propData;
+
+ result = XGetWindowProperty (s->display->display, s->root,
+ d->desktopViewportAtom, 0L, 2L, FALSE,
+ XA_CARDINAL, &actual, &format,
+ &n, &left, &propData);
+
+ if (result == Success && n && propData)
+ {
+ if (n == 2)
+ {
+ memcpy (data, propData, sizeof (unsigned long));
+
+ if (data[0] / s->width < s->size - 1)
+ s->x = data[0] / s->width;
+ }
+
+ XFree (propData);
+ }
+
+ data[0] = s->x * s->width;
+ data[1] = 0;
+
+ XChangeProperty (d->display, s->root, d->desktopViewportAtom,
+ XA_CARDINAL, 32, PropModeReplace,
+ (unsigned char *) data, 2);
+
+ data[0] = s->width * s->size;
+ data[1] = s->height;
+
+ XChangeProperty (d->display, s->root, d->desktopGeometryAtom,
+ XA_CARDINAL, 32, PropModeReplace,
+ (unsigned char *) data, 2);
+
+ data[0] = 1;
+
+ XChangeProperty (d->display, s->root, d->numberOfDesktopsAtom,
+ XA_CARDINAL, 32, PropModeReplace,
+ (unsigned char *) data, 1);
+
+ data[0] = 0;
+
+ XChangeProperty (d->display, s->root, d->currentDesktopAtom,
+ XA_CARDINAL, 32, PropModeReplace,
+ (unsigned char *) data, 1);
+
+ data[0] = 0;
+
+ result = XGetWindowProperty (s->display->display, s->root,
+ d->showingDesktopAtom, 0L, 1L, FALSE,
+ XA_CARDINAL, &actual, &format,
+ &n, &left, &propData);
+
+ if (result == Success && n && propData)
+ {
+ memcpy (data, propData, sizeof (unsigned long));
+ XFree (propData);
+
+ if (data[0])
+ enterShowDesktopMode (s);
+ }
+
+ XChangeProperty (d->display, s->root, d->showingDesktopAtom,
+ XA_CARDINAL, 32, PropModeReplace,
+ (unsigned char *) data, 1);
+}
+
+Bool
+addScreen (CompDisplay *display,
+ int screenNum,
+ Window wmSnSelectionWindow,
+ Atom wmSnAtom,
+ Time wmSnTimestamp)
+{
+ CompScreen *s;
+ Display *dpy = display->display;
+ static char data = 0;
+ XColor black;
+ Pixmap bitmap;
+ XVisualInfo templ;
+ XVisualInfo *visinfo;
+ VisualID visualIDs[MAX_DEPTH + 1];
+ Window rootReturn, parentReturn;
+ Window *children;
+ unsigned int nchildren;
+ int defaultDepth, nvisinfo, value, i;
+ const char *glxExtensions, *glExtensions;
+ GLint stencilBits;
+ XSetWindowAttributes attrib;
+ CompWindow *w;
+
+ s = malloc (sizeof (CompScreen));
+ if (!s)
+ return FALSE;
+
+ s->windowPrivateIndices = 0;
+ s->windowPrivateLen = 0;
+
+ if (display->screenPrivateLen)
+ {
+ s->privates = malloc (display->screenPrivateLen *
+ sizeof (CompPrivate));
+ if (!s->privates)
+ {
+ free (s);
+ return FALSE;
+ }
+ }
+ else
+ s->privates = 0;
+
+ s->display = display;
+
+ compScreenInitOptions (s);
+
+ s->damage = XCreateRegion ();
+ if (!s->damage)
+ return FALSE;
+
+ s->x = 0;
+ s->size = 4;
+
+ s->buttonGrab = 0;
+ s->nButtonGrab = 0;
+ s->keyGrab = 0;
+ s->nKeyGrab = 0;
+
+ s->grabs = 0;
+ s->grabSize = 0;
+ s->maxGrab = 0;
+
+ s->pendingDestroys = 0;
+
+ s->clientList = 0;
+ s->nClientList = 0;
+
+ s->screenNum = screenNum;
+ s->colormap = DefaultColormap (dpy, screenNum);
+ s->root = XRootWindow (dpy, screenNum);
+
+ s->mapNum = 1;
+ s->activeNum = 1;
+
+ s->groups = NULL;
+
+ s->snContext = sn_monitor_context_new (display->snDisplay,
+ screenNum,
+ compScreenSnEvent, s,
+ NULL);
+
+ s->startupSequences = NULL;
+ s->startupSequenceTimeoutHandle = 0;
+
+ s->wmSnSelectionWindow = wmSnSelectionWindow;
+ s->wmSnAtom = wmSnAtom;
+ s->wmSnTimestamp = wmSnTimestamp;
+
+ if (testMode)
+ {
+ XWMHints *wmHints;
+ XSizeHints *normalHints;
+ XClassHint *classHint;
+ int glx_attrib[] = {
+ GLX_RGBA,
+ GLX_RED_SIZE, 1,
+ GLX_STENCIL_SIZE, 2,
+ GLX_DOUBLEBUFFER,
+ None
+ };
+
+ visinfo = glXChooseVisual (dpy, screenNum, glx_attrib);
+ if (!visinfo)
+ {
+ int glx_attrib2[] = {
+ GLX_RGBA,
+ GLX_RED_SIZE, 1,
+ GLX_DOUBLEBUFFER,
+ None
+ };
+
+ visinfo = glXChooseVisual (dpy, screenNum, glx_attrib2);
+ if (!visinfo)
+ {
+ fprintf (stderr, "%s: Couldn't find a double buffered "
+ "RGB visual.\n", programName);
+ return FALSE;
+ }
+ }
+
+ attrib.colormap = XCreateColormap (dpy, s->root, visinfo->visual,
+ AllocNone);
+
+ normalHints = XAllocSizeHints ();
+ normalHints->flags = 0;
+ normalHints->x = 0;
+ normalHints->y = 0;
+ normalHints->width = 800;
+ normalHints->height = 600;
+
+ classHint = XAllocClassHint ();
+ classHint->res_name = "compiz";
+ classHint->res_class = "Compiz";
+
+ wmHints = XAllocWMHints ();
+ wmHints->flags = InputHint;
+ wmHints->input = TRUE;
+
+ s->root = XCreateWindow (dpy, s->root, 0, 0,
+ normalHints->width, normalHints->height, 0,
+ visinfo->depth, InputOutput, visinfo->visual,
+ CWColormap, &attrib);
+
+ XSelectInput (dpy, s->root,
+ SubstructureNotifyMask |
+ ExposureMask |
+ ButtonPressMask |
+ ButtonReleaseMask |
+ ButtonMotionMask);
+
+ XRRSelectInput (dpy, s->root, RRScreenChangeNotifyMask);
+
+ XSetWMProtocols (dpy, s->root, &display->wmDeleteWindowAtom, 1);
+
+ XmbSetWMProperties (dpy, s->root,
+ "glxcompmgr - Test mode", "glxcompmgr",
+ programArgv, programArgc,
+ normalHints, wmHints, classHint);
+
+ XMapWindow (dpy, s->root);
+
+ XFree (wmHints);
+ XFree (classHint);
+ XFree (normalHints);
+ }
+
+ s->fake[0] = s->fake[1] = 0;
+
+ s->escapeKeyCode = XKeysymToKeycode (display->display,
+ XStringToKeysym ("Escape"));
+
+ s->damageMask = COMP_SCREEN_DAMAGE_ALL_MASK;
+ s->next = 0;
+ s->exposeRects = 0;
+ s->sizeExpose = 0;
+ s->nExpose = 0;
+
+ s->rasterX = 0;
+ s->rasterY = 0;
+
+ s->windows = 0;
+ s->reverseWindows = 0;
+
+ s->stencilRef = 0x1;
+
+ s->nextRedraw = 0;
+ s->frameStatus = 0;
+
+ s->showingDesktopMask = 0;
+
+ gettimeofday (&s->lastRedraw, 0);
+
+ s->setScreenOption = setScreenOption;
+ s->setScreenOptionForPlugin = setScreenOptionForPlugin;
+
+ s->initPluginForScreen = initPluginForScreen;
+ s->finiPluginForScreen = finiPluginForScreen;
+
+ s->preparePaintScreen = preparePaintScreen;
+ s->donePaintScreen = donePaintScreen;
+ s->paintScreen = paintScreen;
+ s->paintTransformedScreen = paintTransformedScreen;
+ s->paintBackground = paintBackground;
+ s->paintWindow = paintWindow;
+ s->addWindowGeometry = addWindowGeometry;
+ s->drawWindowGeometry = drawWindowGeometry;
+ s->damageWindowRect = damageWindowRect;
+ s->focusWindow = focusWindow;
+ s->setWindowScale = setWindowScale;
+
+ s->windowResizeNotify = windowResizeNotify;
+ s->windowMoveNotify = windowMoveNotify;
+ s->windowGrabNotify = windowGrabNotify;
+ s->windowUngrabNotify = windowUngrabNotify;
+
+ s->getProcAddress = 0;
+
+ if (!XGetWindowAttributes (dpy, s->root, &s->attrib))
+ return FALSE;
+
+ s->workArea.x = 0;
+ s->workArea.y = 0;
+ s->workArea.width = s->attrib.width;
+ s->workArea.height = s->attrib.height;
+
+ s->grabWindow = None;
+
+ templ.visualid = XVisualIDFromVisual (s->attrib.visual);
+
+ visinfo = XGetVisualInfo (dpy, VisualIDMask, &templ, &nvisinfo);
+ if (!nvisinfo)
+ {
+ fprintf (stderr, "%s: Couldn't get visual info for default visual\n",
+ programName);
+ return FALSE;
+ }
+
+ defaultDepth = visinfo->depth;
+
+ black.red = black.green = black.blue = 0;
+
+ if (!XAllocColor (dpy, s->colormap, &black))
+ {
+ fprintf (stderr, "%s: Couldn't allocate color\n", programName);
+ return FALSE;
+ }
+
+ bitmap = XCreateBitmapFromData (dpy, s->root, &data, 1, 1);
+ if (!bitmap)
+ {
+ fprintf (stderr, "%s: Couldn't create bitmap\n", programName);
+ return FALSE;
+ }
+
+ s->invisibleCursor = XCreatePixmapCursor (dpy, bitmap, bitmap,
+ &black, &black, 0, 0);
+ if (!s->invisibleCursor)
+ {
+ fprintf (stderr, "%s: Couldn't create invisible cursor\n",
+ programName);
+ return FALSE;
+ }
+
+ XFreePixmap (dpy, bitmap);
+ XFreeColors (dpy, s->colormap, &black.pixel, 1, 0);
+
+ glXGetConfig (dpy, visinfo, GLX_USE_GL, &value);
+ if (!value)
+ {
+ fprintf (stderr, "%s: Root visual is not a GL visual\n",
+ programName);
+ return FALSE;
+ }
+
+ addScreenBinding (s, &s->opt[COMP_SCREEN_OPTION_CLOSE_WINDOW].value.bind);
+ addScreenBinding (s, &s->opt[COMP_SCREEN_OPTION_MAIN_MENU].value.bind);
+ addScreenBinding (s, &s->opt[COMP_SCREEN_OPTION_RUN_DIALOG].value.bind);
+
+ glXGetConfig (dpy, visinfo, GLX_DOUBLEBUFFER, &value);
+ if (!value)
+ {
+ fprintf (stderr,
+ "%s: Root visual is not a double buffered GL visual\n",
+ programName);
+ return FALSE;
+ }
+
+ s->ctx = glXCreateContext (dpy, visinfo, NULL, TRUE);
+ if (!s->ctx)
+ {
+ fprintf (stderr, "%s: glXCreateContext failed\n", programName);
+ return FALSE;
+ }
+
+ XFree (visinfo);
+
+ /* we don't want to allocate back, stencil or depth buffers for pixmaps
+ so lets see if we can find an approriate visual without these buffers */
+ for (i = 0; i <= MAX_DEPTH; i++)
+ {
+ int j, db, stencil, depth;
+
+ visualIDs[i] = 0;
+
+ db = MAXSHORT;
+ stencil = MAXSHORT;
+ depth = MAXSHORT;
+
+ templ.depth = i;
+
+ visinfo = XGetVisualInfo (dpy, VisualDepthMask, &templ, &nvisinfo);
+ for (j = 0; j < nvisinfo; j++)
+ {
+ glXGetConfig (dpy, &visinfo[j], GLX_USE_GL, &value);
+ if (!value)
+ continue;
+
+ glXGetConfig (dpy, &visinfo[j], GLX_DOUBLEBUFFER, &value);
+ if (value > db)
+ continue;
+
+ db = value;
+ glXGetConfig (dpy, &visinfo[j], GLX_STENCIL_SIZE, &value);
+ if (value > stencil)
+ continue;
+
+ stencil = value;
+ glXGetConfig (dpy, &visinfo[j], GLX_DEPTH_SIZE, &value);
+ if (value > depth)
+ continue;
+
+ depth = value;
+ visualIDs[i] = visinfo[j].visualid;
+ }
+
+ if (nvisinfo)
+ XFree (visinfo);
+ }
+
+ /* create contexts for supported depths */
+ for (i = 0; i <= MAX_DEPTH; i++)
+ {
+ templ.visualid = visualIDs[i];
+ s->glxPixmapVisuals[i] = XGetVisualInfo (dpy,
+ VisualIDMask,
+ &templ,
+ &nvisinfo);
+ }
+
+ if (!s->glxPixmapVisuals[defaultDepth])
+ {
+ fprintf (stderr, "%s: No GL visual for default depth, "
+ "this isn't going to work.\n", programName);
+ return FALSE;
+ }
+
+ glXMakeCurrent (dpy, s->root, s->ctx);
+ currentRoot = s->root;
+
+ glxExtensions = glXQueryExtensionsString (s->display->display, screenNum);
+ if (!testMode && !strstr (glxExtensions, "GLX_EXT_texture_from_pixmap"))
+ {
+ fprintf (stderr, "%s: GLX_EXT_texture_from_pixmap is missing\n",
+ programName);
+ return FALSE;
+ }
+
+ s->getProcAddress = (GLXGetProcAddressProc)
+ getProcAddress (s, "glXGetProcAddressARB");
+ s->bindTexImage = (GLXBindTexImageProc)
+ getProcAddress (s, "glXBindTexImageEXT");
+ s->releaseTexImage = (GLXReleaseTexImageProc)
+ getProcAddress (s, "glXReleaseTexImageEXT");
+ s->queryDrawable = (GLXQueryDrawableProc)
+ getProcAddress (s, "glXQueryDrawable");
+
+ if (!testMode && !s->bindTexImage)
+ {
+ fprintf (stderr, "%s: glXBindTexImageEXT is missing\n", programName);
+ return FALSE;
+ }
+
+ if (!testMode && !s->releaseTexImage)
+ {
+ fprintf (stderr, "%s: glXReleaseTexImageEXT is missing\n",
+ programName);
+ return FALSE;
+ }
+
+ if (!testMode && !s->queryDrawable)
+ {
+ fprintf (stderr, "%s: glXQueryDrawable is missing\n", programName);
+ return FALSE;
+ }
+
+ s->textureRectangle = 0;
+ glExtensions = (const char *) glGetString (GL_EXTENSIONS);
+ if (strstr (glExtensions, "GL_NV_texture_rectangle") ||
+ strstr (glExtensions, "GL_EXT_texture_rectangle") ||
+ strstr (glExtensions, "GL_ARB_texture_rectangle"))
+ s->textureRectangle = 1;
+
+ s->textureNonPowerOfTwo = 0;
+ if (strstr (glExtensions, "GL_ARB_texture_non_power_of_two"))
+ s->textureNonPowerOfTwo = 1;
+
+ if (!(s->textureRectangle || s->textureNonPowerOfTwo))
+ {
+ fprintf (stderr, "%s: Support for non power of two textures missing\n",
+ programName);
+ return FALSE;
+ }
+
+ s->textureEnvCombine = s->textureEnvCrossbar = 0;
+ if (strstr (glExtensions, "GL_ARB_texture_env_combine"))
+ {
+ s->textureEnvCombine = 1;
+
+ /* XXX: GL_NV_texture_env_combine4 need special code but it seams to
+ be working anyway for now... */
+ if (strstr (glExtensions, "GL_ARB_texture_env_crossbar") ||
+ strstr (glExtensions, "GL_NV_texture_env_combine4"))
+ s->textureEnvCrossbar = 1;
+ }
+
+ s->textureBorderClamp = 0;
+ if (strstr (glExtensions, "GL_ARB_texture_border_clamp") ||
+ strstr (glExtensions, "GL_SGIS_texture_border_clamp"))
+ s->textureBorderClamp = 1;
+
+ s->maxTextureUnits = 1;
+ if (strstr (glExtensions, "GL_ARB_multitexture"))
+ {
+ s->activeTexture = (GLActiveTextureProc)
+ getProcAddress (s, "glActiveTexture");
+ s->clientActiveTexture = (GLClientActiveTextureProc)
+ getProcAddress (s, "glClientActiveTexture");
+
+ if (s->activeTexture && s->clientActiveTexture)
+ glGetIntegerv (GL_MAX_TEXTURE_UNITS_ARB, &s->maxTextureUnits);
+ }
+
+ initTexture (s, &s->backgroundTexture);
+
+ s->desktopWindowCount = 0;
+
+ glGetIntegerv (GL_STENCIL_BITS, &stencilBits);
+ if (!stencilBits)
+ {
+ fprintf (stderr, "%s: No stencil buffer. Clipping of transformed "
+ "windows is not going to be correct when screen is "
+ "transformed.\n", programName);
+ }
+
+ glClearColor (0.0, 0.0, 0.0, 1.0);
+ glBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable (GL_CULL_FACE);
+ glDisable (GL_BLEND);
+ glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+ glColor4usv (defaultColor);
+ glEnableClientState (GL_VERTEX_ARRAY);
+ glEnableClientState (GL_TEXTURE_COORD_ARRAY);
+
+ s->canDoSaturated = s->canDoSlightlySaturated = FALSE;
+ if (s->textureEnvCombine && s->maxTextureUnits >= 2)
+ {
+ s->canDoSaturated = TRUE;
+ if (s->textureEnvCrossbar && s->maxTextureUnits >= 4)
+ s->canDoSlightlySaturated = TRUE;
+ }
+
+ s->redrawTime = 1000 / defaultRefreshRate;
+ s->optimalRedrawTime = s->redrawTime;
+
+ reshape (s, s->attrib.width, s->attrib.height);
+
+ s->next = display->screens;
+ display->screens = s;
+
+ screenInitPlugins (s);
+
+ detectRefreshRateOfScreen (s);
+
+ XGrabServer (dpy);
+
+ XQueryTree (dpy, s->root,
+ &rootReturn, &parentReturn,
+ &children, &nchildren);
+
+ for (i = 0; i < nchildren; i++)
+ addWindow (s, children[i], i ? children[i - 1] : 0);
+
+ for (w = s->windows; w; w = w->next)
+ {
+ if (w->attrib.map_state == IsViewable)
+ {
+ w->activeNum = s->activeNum++;
+ w->damaged = TRUE;
+ w->placed = TRUE;
+ w->invisible = WINDOW_INVISIBLE (w);
+ }
+ else if (w->state & CompWindowStateHiddenMask)
+ {
+ w->placed = TRUE;
+ }
+ }
+
+ XUngrabServer (dpy);
+
+ XFree (children);
+
+ attrib.override_redirect = 1;
+ s->grabWindow = XCreateWindow (dpy, s->root, -100, -100, 1, 1, 0,
+ CopyFromParent, InputOnly,
+ CopyFromParent, CWOverrideRedirect,
+ &attrib);
+
+ XMapWindow (dpy, s->grabWindow);
+
+ if (testMode)
+ {
+ s->fake[0] = XCreateWindow (dpy, s->root, 64, 32, 1, 1, 0,
+ CopyFromParent, InputOutput,
+ CopyFromParent, 0, NULL);
+
+ s->fake[1] = XCreateWindow (dpy, s->root, 256, 256, 1, 1, 0,
+ CopyFromParent, InputOutput,
+ CopyFromParent, 0, NULL);
+ }
+
+ setDesktopHints (s);
+ setSupportingWmCheck (s);
+ setSupported (s);
+
+ s->normalCursor = XCreateFontCursor (dpy, XC_left_ptr);
+ s->busyCursor = XCreateFontCursor (dpy, XC_watch);
+
+ XDefineCursor (dpy, s->root, s->normalCursor);
+
+ return TRUE;
+}
+
+void
+damageScreenRegion (CompScreen *screen,
+ Region region)
+{
+ if (screen->damageMask & COMP_SCREEN_DAMAGE_ALL_MASK)
+ return;
+
+ XUnionRegion (screen->damage, region, screen->damage);
+
+ screen->damageMask |= COMP_SCREEN_DAMAGE_REGION_MASK;
+}
+
+void
+damageScreen (CompScreen *s)
+{
+ s->damageMask |= COMP_SCREEN_DAMAGE_ALL_MASK;
+ s->damageMask &= ~COMP_SCREEN_DAMAGE_REGION_MASK;
+}
+
+void
+damagePendingOnScreen (CompScreen *s)
+{
+ s->damageMask |= COMP_SCREEN_DAMAGE_PENDING_MASK;
+}
+
+void
+forEachWindowOnScreen (CompScreen *screen,
+ ForEachWindowProc proc,
+ void *closure)
+{
+ CompWindow *w;
+
+ for (w = screen->windows; w; w = w->next)
+ (*proc) (w, closure);
+}
+
+CompWindow *
+findWindowAtScreen (CompScreen *s,
+ Window id)
+{
+ if (lastFoundWindow && lastFoundWindow->id == id)
+ {
+ return lastFoundWindow;
+ }
+ else
+ {
+ CompWindow *w;
+
+ for (w = s->windows; w; w = w->next)
+ if (w->id == id)
+ return (lastFoundWindow = w);
+ }
+
+ return 0;
+}
+
+CompWindow *
+findTopLevelWindowAtScreen (CompScreen *s,
+ Window id)
+{
+ CompWindow *found = NULL;
+
+ if (lastFoundWindow && lastFoundWindow->id == id)
+ {
+ found = lastFoundWindow;
+ }
+ else
+ {
+ CompWindow *w;
+
+ for (w = s->windows; w; w = w->next)
+ {
+ if (w->id == id)
+ {
+ found = (lastFoundWindow = w);
+ break;
+ }
+ }
+ }
+
+ if (!found)
+ return NULL;
+
+ if (found->attrib.override_redirect)
+ {
+ /* likely a frame window */
+ if (found->attrib.class == InputOnly)
+ {
+ CompWindow *w;
+
+ for (w = s->windows; w; w = w->next)
+ if (w->frame == id)
+ return w;
+ }
+
+ return NULL;
+ }
+
+ return found;
+}
+
+void
+insertWindowIntoScreen (CompScreen *s,
+ CompWindow *w,
+ Window aboveId)
+{
+ CompWindow *p;
+
+ if (s->windows)
+ {
+ if (!aboveId)
+ {
+ w->next = s->windows;
+ w->prev = NULL;
+ s->windows->prev = w;
+ s->windows = w;
+ }
+ else
+ {
+ for (p = s->windows; p; p = p->next)
+ {
+ if (p->next)
+ {
+ if (p->id == aboveId && p->mapNum)
+ {
+ w->next = p->next;
+ w->prev = p;
+ p->next->prev = w;
+ p->next = w;
+
+ break;
+ }
+ }
+ else
+ {
+ p->next = w;
+ w->next = NULL;
+ w->prev = p;
+ s->reverseWindows = w;
+
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ s->reverseWindows = s->windows = w;
+ w->prev = w->next = NULL;
+ }
+}
+
+void
+unhookWindowFromScreen (CompScreen *s,
+ CompWindow *w)
+{
+ CompWindow *next, *prev;
+
+ next = w->next;
+ prev = w->prev;
+
+ if (next || prev)
+ {
+ if (next)
+ {
+ if (prev)
+ {
+ next->prev = prev;
+ }
+ else
+ {
+ s->windows = next;
+ next->prev = NULL;
+ }
+ }
+
+ if (prev)
+ {
+ if (next)
+ {
+ prev->next = next;
+ }
+ else
+ {
+ s->reverseWindows = prev;
+ prev->next = NULL;
+ }
+ }
+ }
+ else
+ {
+ s->windows = s->reverseWindows = NULL;
+ }
+
+ if (w == lastFoundWindow)
+ lastFoundWindow = NULL;
+ if (w == lastDamagedWindow)
+ lastDamagedWindow = NULL;
+}
+
+#define POINTER_GRAB_MASK (ButtonReleaseMask | \
+ ButtonPressMask | \
+ PointerMotionMask)
+int
+pushScreenGrab (CompScreen *s,
+ Cursor cursor)
+{
+ if (s->maxGrab == 0)
+ {
+ int status;
+
+ status = XGrabPointer (s->display->display, s->grabWindow, TRUE,
+ POINTER_GRAB_MASK,
+ GrabModeAsync, GrabModeAsync,
+ s->root, cursor,
+ CurrentTime);
+
+ if (status == GrabSuccess)
+ {
+ status = XGrabKeyboard (s->display->display,
+ s->grabWindow, TRUE,
+ GrabModeAsync, GrabModeAsync,
+ CurrentTime);
+ if (status != GrabSuccess)
+ {
+ XUngrabPointer (s->display->display, CurrentTime);
+ return 0;
+ }
+ }
+ else
+ return 0;
+ }
+ else
+ {
+ XChangeActivePointerGrab (s->display->display, POINTER_GRAB_MASK,
+ cursor, CurrentTime);
+ }
+
+ if (s->grabSize <= s->maxGrab)
+ {
+ s->grabs = realloc (s->grabs, sizeof (CompGrab) * (s->maxGrab + 1));
+ if (!s->grabs)
+ return 0;
+
+ s->grabSize = s->maxGrab + 1;
+ }
+
+ s->grabs[s->maxGrab].cursor = cursor;
+ s->grabs[s->maxGrab].active = TRUE;
+
+ s->maxGrab++;
+
+ return s->maxGrab;
+}
+
+void
+removeScreenGrab (CompScreen *s,
+ int index,
+ XPoint *restorePointer)
+{
+ int maxGrab;
+
+ index--;
+ if (index < 0 || index >= s->maxGrab)
+ abort ();
+
+ s->grabs[index].cursor = None;
+ s->grabs[index].active = FALSE;
+
+ for (maxGrab = s->maxGrab; maxGrab; maxGrab--)
+ if (s->grabs[maxGrab - 1].active)
+ break;
+
+ if (maxGrab != s->maxGrab)
+ {
+ if (maxGrab)
+ {
+ XChangeActivePointerGrab (s->display->display,
+ POINTER_GRAB_MASK,
+ s->grabs[s->maxGrab - 1].cursor,
+ CurrentTime);
+ }
+ else
+ {
+ if (restorePointer)
+ XWarpPointer (s->display->display, None, s->root, 0, 0, 0, 0,
+ restorePointer->x, restorePointer->y);
+
+ XUngrabPointer (s->display->display, CurrentTime);
+ XUngrabKeyboard (s->display->display, CurrentTime);
+ }
+ s->maxGrab = maxGrab;
+ }
+}
+
+static Bool
+addPassiveKeyGrab (CompScreen *s,
+ CompKeyBinding *key)
+{
+ CompKeyGrab *keyGrab;
+ unsigned int modifiers, mask, ignore;
+ int i;
+
+ modifiers = key->modifiers & ~(CompPressMask | CompReleaseMask);
+ if (modifiers == key->modifiers)
+ return TRUE;
+
+ for (i = 0; i < s->nKeyGrab; i++)
+ {
+ if (key->keycode == s->keyGrab[i].keycode &&
+ modifiers == s->keyGrab[i].modifiers)
+ {
+ s->keyGrab[i].count++;
+ return TRUE;
+ }
+ }
+
+ keyGrab = realloc (s->keyGrab, sizeof (CompKeyGrab) * (s->nKeyGrab + 1));
+ if (!keyGrab)
+ return FALSE;
+
+ s->keyGrab = keyGrab;
+
+ mask = virtualToRealModMask (s->display, modifiers);
+ if (!(mask & CompNoMask))
+ {
+ compCheckForError (s->display->display);
+
+ for (ignore = 0; ignore <= s->display->ignoredModMask; ignore++)
+ {
+ if (ignore & ~s->display->ignoredModMask)
+ continue;
+
+ XGrabKey (s->display->display,
+ key->keycode,
+ mask | ignore,
+ s->root,
+ TRUE,
+ GrabModeAsync,
+ GrabModeAsync);
+ }
+
+ if (compCheckForError (s->display->display))
+ {
+
+#ifdef DEBUG
+ KeySym keysym;
+ char *keyname;
+
+ keysym = XKeycodeToKeysym (s->display->display,
+ key->keycode,
+ 0);
+ keyname = XKeysymToString (keysym);
+
+ fprintf (stderr, "XGrabKey failed: %s 0x%x\n",
+ keyname, modifiers);
+#endif
+
+ return FALSE;
+ }
+ }
+
+ s->keyGrab[s->nKeyGrab].keycode = key->keycode;
+ s->keyGrab[s->nKeyGrab].modifiers = modifiers;
+ s->keyGrab[s->nKeyGrab].count = 1;
+
+ s->nKeyGrab++;
+
+ return TRUE;
+}
+
+static void
+removePassiveKeyGrab (CompScreen *s,
+ CompKeyBinding *key)
+{
+ unsigned int modifiers, mask, ignore;
+ int i;
+
+ modifiers = key->modifiers & ~(CompPressMask | CompReleaseMask);
+ if (modifiers == key->modifiers)
+ return;
+
+ for (i = 0; i < s->nKeyGrab; i++)
+ {
+ if (key->keycode == s->keyGrab[i].keycode &&
+ modifiers == s->keyGrab[i].modifiers)
+ {
+ s->keyGrab[i].count--;
+ if (s->keyGrab[i].count)
+ return;
+
+ s->nKeyGrab--;
+ s->keyGrab = realloc (s->keyGrab,
+ sizeof (CompKeyGrab) * s->nKeyGrab);
+
+ mask = virtualToRealModMask (s->display, modifiers);
+ if (!(mask & CompNoMask))
+ {
+ for (ignore = 0; ignore <= s->display->ignoredModMask; ignore++)
+ {
+ if (ignore & ~s->display->ignoredModMask)
+ continue;
+
+ XUngrabKey (s->display->display,
+ key->keycode,
+ mask | ignore,
+ s->root);
+ }
+ }
+ }
+ }
+}
+
+static void
+updatePassiveKeyGrabs (CompScreen *s)
+{
+ unsigned int mask, ignore;
+ int i;
+
+ XUngrabKey (s->display->display, AnyKey, AnyModifier, s->root);
+
+ for (i = 0; i < s->nKeyGrab; i++)
+ {
+ mask = virtualToRealModMask (s->display, s->keyGrab[i].modifiers);
+ if (!(mask & CompNoMask))
+ {
+ for (ignore = 0; ignore <= s->display->ignoredModMask; ignore++)
+ {
+ if (ignore & ~s->display->ignoredModMask)
+ continue;
+
+ XGrabKey (s->display->display,
+ s->keyGrab[i].keycode,
+ mask | ignore,
+ s->root,
+ TRUE,
+ GrabModeAsync,
+ GrabModeAsync);
+ }
+ }
+ }
+}
+
+static Bool
+addPassiveButtonGrab (CompScreen *s,
+ CompButtonBinding *button)
+{
+ CompButtonGrab *buttonGrab;
+ unsigned int modifiers;
+ int i;
+
+ modifiers = button->modifiers & ~(CompPressMask | CompReleaseMask);
+ if (modifiers == button->modifiers)
+ return TRUE;
+
+ for (i = 0; i < s->nButtonGrab; i++)
+ {
+ if (button->button == s->buttonGrab[i].button &&
+ modifiers == s->buttonGrab[i].modifiers)
+ {
+ s->buttonGrab[i].count++;
+ return TRUE;
+ }
+ }
+
+ buttonGrab = realloc (s->buttonGrab,
+ sizeof (CompButtonGrab) * (s->nButtonGrab + 1));
+ if (!buttonGrab)
+ return FALSE;
+
+ s->buttonGrab = buttonGrab;
+
+ s->buttonGrab[s->nButtonGrab].button = button->button;
+ s->buttonGrab[s->nButtonGrab].modifiers = modifiers;
+ s->buttonGrab[s->nButtonGrab].count = 1;
+
+ s->nButtonGrab++;
+
+ return TRUE;
+}
+
+static void
+removePassiveButtonGrab (CompScreen *s,
+ CompButtonBinding *button)
+{
+ unsigned int modifiers;
+ int i;
+
+ modifiers = button->modifiers & ~(CompPressMask | CompReleaseMask);
+ if (modifiers == button->modifiers)
+ return;
+
+ for (i = 0; i < s->nButtonGrab; i++)
+ {
+ if (button->button == s->buttonGrab[i].button &&
+ modifiers == s->buttonGrab[i].modifiers)
+ {
+ s->buttonGrab[i].count--;
+ if (s->buttonGrab[i].count)
+ return;
+
+ s->nButtonGrab--;
+ s->buttonGrab = realloc (s->buttonGrab,
+ sizeof (CompButtonGrab) * s->nButtonGrab);
+ }
+ }
+}
+
+Bool
+addScreenBinding (CompScreen *s,
+ CompBinding *binding)
+{
+ if (binding->type == CompBindingTypeKey)
+ return addPassiveKeyGrab (s, &binding->u.key);
+ else if (binding->type == CompBindingTypeButton)
+ return addPassiveButtonGrab (s, &binding->u.button);
+
+ return FALSE;
+}
+
+void
+removeScreenBinding (CompScreen *s,
+ CompBinding *binding)
+{
+ if (binding->type == CompBindingTypeKey)
+ removePassiveKeyGrab (s, &binding->u.key);
+ else if (binding->type == CompBindingTypeButton)
+ removePassiveButtonGrab (s, &binding->u.button);
+}
+
+void
+updatePassiveGrabs (CompScreen *s)
+{
+ updatePassiveKeyGrabs (s);
+}
+
+void
+updateWorkareaForScreen (CompScreen *s)
+{
+ CompWindow *w;
+ int leftStrut, rightStrut, topStrut, bottomStrut;
+ XRectangle workArea;
+
+ leftStrut = 0;
+ rightStrut = 0;
+ topStrut = 0;
+ bottomStrut = 0;
+
+ for (w = s->windows; w; w = w->next)
+ {
+ if (w->attrib.map_state == IsUnmapped)
+ continue;
+
+ if (w->struts)
+ {
+ if (w->struts->left.width > leftStrut)
+ leftStrut = w->struts->left.width;
+
+ if (w->struts->right.width > rightStrut)
+ rightStrut = w->struts->right.width;
+
+ if (w->struts->top.height > topStrut)
+ topStrut = w->struts->top.height;
+
+ if (w->struts->bottom.height > bottomStrut)
+ bottomStrut = w->struts->bottom.height;
+ }
+ }
+
+#define MIN_SANE_AREA 100
+
+ if ((leftStrut + rightStrut) > (s->width - MIN_SANE_AREA))
+ {
+ leftStrut = (s->width - MIN_SANE_AREA) / 2;
+ rightStrut = leftStrut;
+ }
+
+ if ((topStrut + bottomStrut) > (s->height - MIN_SANE_AREA))
+ {
+ topStrut = (s->height - MIN_SANE_AREA) / 2;
+ bottomStrut = topStrut;
+ }
+
+ workArea.x = leftStrut;
+ workArea.y = topStrut;
+ workArea.width = s->width - leftStrut - rightStrut;
+ workArea.height = s->height - topStrut - bottomStrut;
+
+ if (memcmp (&workArea, &s->workArea, sizeof (XRectangle)))
+ {
+ unsigned long data[4];
+
+ data[0] = workArea.x;
+ data[1] = workArea.y;
+ data[2] = workArea.width;
+ data[3] = workArea.height;
+
+ XChangeProperty (s->display->display, s->root,
+ s->display->workareaAtom, XA_CARDINAL, 32,
+ PropModeReplace, (unsigned char *) data, 4);
+
+ s->workArea = workArea;
+ }
+}
+
+static Bool
+isClientListWindow (CompWindow *w)
+{
+ if (w->attrib.override_redirect)
+ return FALSE;
+
+ if (w->attrib.map_state != IsViewable)
+ {
+ if (!(w->state & CompWindowStateHiddenMask))
+ return FALSE;
+ }
+
+ if (w->type & CompWindowTypeNormalMask)
+ return TRUE;
+
+ return FALSE;
+}
+
+static void
+countClientListWindow (CompWindow *w,
+ void *closure)
+{
+ if (isClientListWindow (w))
+ {
+ int *num = (int *) closure;
+
+ *num = *num + 1;
+ }
+}
+
+static void
+addClientListWindow (CompWindow *w,
+ void *closure)
+{
+ if (isClientListWindow (w))
+ {
+ int *num = (int *) closure;
+
+ w->screen->clientList[*num] = w;
+ *num = *num + 1;
+ }
+}
+
+static int
+compareMappingOrder (const void *w1,
+ const void *w2)
+{
+ return (*((CompWindow **) w1))->mapNum - (*((CompWindow **) w2))->mapNum;
+}
+
+void
+updateClientListForScreen (CompScreen *s)
+{
+ Window *clientList;
+ Window *clientListStacking;
+ Bool updateClientList = FALSE;
+ Bool updateClientListStacking = FALSE;
+ int i, n = 0;
+
+ forEachWindowOnScreen (s, countClientListWindow, (void *) &n);
+
+ if (n == 0)
+ {
+ if (n != s->nClientList)
+ {
+ free (s->clientList);
+
+ s->clientList = NULL;
+ s->nClientList = 0;
+
+ XChangeProperty (s->display->display, s->root,
+ s->display->clientListAtom,
+ XA_WINDOW, 32, PropModeReplace,
+ (unsigned char *) &s->grabWindow, 1);
+ XChangeProperty (s->display->display, s->root,
+ s->display->clientListStackingAtom,
+ XA_WINDOW, 32, PropModeReplace,
+ (unsigned char *) &s->grabWindow, 1);
+ }
+
+ return;
+ }
+
+ if (n != s->nClientList)
+ {
+ CompWindow **list;
+
+ list = realloc (s->clientList,
+ (sizeof (CompWindow *) + sizeof (Window) * 2) * n);
+ if (!list)
+ return;
+
+ s->clientList = list;
+ s->nClientList = n;
+
+ updateClientList = updateClientListStacking = TRUE;
+ }
+
+ clientList = (Window *) (s->clientList + n);
+ clientListStacking = clientList + n;
+
+ i = 0;
+ forEachWindowOnScreen (s, addClientListWindow, (void *) &i);
+
+ for (i = 0; i < n; i++)
+ {
+ if (!updateClientListStacking)
+ {
+ if (clientListStacking[i] != s->clientList[i]->id)
+ updateClientListStacking = TRUE;
+ }
+
+ clientListStacking[i] = s->clientList[i]->id;
+ }
+
+ /* sort window list in mapping order */
+ qsort (s->clientList, n, sizeof (CompWindow *), compareMappingOrder);
+
+ for (i = 0; i < n; i++)
+ {
+ if (!updateClientList)
+ {
+ if (clientList[i] != s->clientList[i]->id)
+ updateClientList = TRUE;
+ }
+
+ clientList[i] = s->clientList[i]->id;
+ }
+
+ if (updateClientList)
+ XChangeProperty (s->display->display, s->root,
+ s->display->clientListAtom,
+ XA_WINDOW, 32, PropModeReplace,
+ (unsigned char *) clientList, s->nClientList);
+
+ if (updateClientListStacking)
+ XChangeProperty (s->display->display, s->root,
+ s->display->clientListStackingAtom,
+ XA_WINDOW, 32, PropModeReplace,
+ (unsigned char *) clientListStacking, s->nClientList);
+}
+
+Window
+getActiveWindow (CompDisplay *display,
+ Window root)
+{
+ Atom actual;
+ int result, format;
+ unsigned long n, left;
+ unsigned char *data;
+ Window w = None;
+
+ result = XGetWindowProperty (display->display, root,
+ display->winActiveAtom, 0L, 1L, FALSE,
+ XA_WINDOW, &actual, &format,
+ &n, &left, &data);
+
+ if (result == Success && n && data)
+ {
+ memcpy (&w, data, sizeof (Window));
+ XFree (data);
+ }
+
+ return w;
+}
+
+void
+closeActiveWindow (CompScreen *s)
+{
+ CompWindow *w;
+
+ w = findWindowAtDisplay (s->display, s->display->activeWindow);
+ if (w)
+ closeWindow (w);
+}
+
+void
+panelAction (CompScreen *s,
+ Atom panelAction)
+{
+ XEvent ev;
+
+ ev.type = ClientMessage;
+ ev.xclient.window = s->root;
+ ev.xclient.message_type = s->display->panelActionAtom;
+ ev.xclient.format = 32;
+ ev.xclient.data.l[0] = panelAction;
+ ev.xclient.data.l[1] = CurrentTime;
+ ev.xclient.data.l[2] = 0;
+ ev.xclient.data.l[3] = 0;
+ ev.xclient.data.l[4] = 0;
+
+ XUngrabPointer (s->display->display, CurrentTime);
+ XUngrabKeyboard (s->display->display, CurrentTime);
+
+ XSendEvent (s->display->display, s->root, FALSE, StructureNotifyMask, &ev);
+}
+
+void
+runCommand (CompScreen *s,
+ const char *command)
+{
+ if (*command == '\0')
+ return;
+
+ if (fork () == 0)
+ {
+ setsid ();
+ putenv (s->display->displayString);
+ execl ("/bin/sh", "/bin/sh", "-c", command, NULL);
+ exit (0);
+ }
+}
+
+void
+moveScreenViewport (CompScreen *s,
+ int tx,
+ Bool sync)
+{
+ tx = s->x - tx;
+ tx = MOD (tx, s->size);
+ tx -= s->x;
+
+ if (tx)
+ {
+ CompWindow *w;
+ int m, wx, vWidth = s->width * s->size;
+
+ s->x += tx;
+
+ tx *= -s->width;
+
+ for (w = s->windows; w; w = w->next)
+ {
+ if (w->attrib.map_state != IsViewable)
+ continue;
+
+ if (w->attrib.override_redirect)
+ continue;
+
+ if (w->type & (CompWindowTypeDesktopMask | CompWindowTypeDockMask))
+ continue;
+
+ if (w->state & CompWindowStateStickyMask)
+ continue;
+
+ m = w->attrib.x + tx;
+ if (m - w->output.left < s->width - vWidth)
+ wx = tx + vWidth;
+ else if (m + w->width + w->output.right > vWidth)
+ wx = tx - vWidth;
+ else
+ wx = tx;
+
+ if (w->saveMask & CWX)
+ w->saveWc.x += wx;
+
+ moveWindow (w, wx, 0, sync);
+
+ if (sync)
+ syncWindowPosition (w);
+ }
+
+ if (sync)
+ {
+ unsigned long data[2];
+
+ data[0] = s->x * s->width;
+ data[1] = 0;
+
+ XChangeProperty (s->display->display, s->root,
+ s->display->desktopViewportAtom,
+ XA_CARDINAL, 32, PropModeReplace,
+ (unsigned char *) data, 2);
+ }
+ }
+}
+
+void
+moveWindowToViewportPosition (CompWindow *w,
+ int x,
+ Bool sync)
+{
+ int tx, vWidth = w->screen->width * w->screen->size;
+
+ x += w->screen->x * w->screen->width;
+ x = MOD (x, vWidth);
+ x -= w->screen->x * w->screen->width;
+
+ tx = x - w->attrib.x;
+ if (tx)
+ {
+ int m, wx;
+
+ if (w->attrib.map_state != IsViewable)
+ return;
+
+ if (w->attrib.override_redirect)
+ return;
+
+ if (w->type & (CompWindowTypeDesktopMask | CompWindowTypeDockMask))
+ return;
+
+ if (w->state & CompWindowStateStickyMask)
+ return;
+
+ m = w->attrib.x + tx;
+ if (m - w->output.left < w->screen->width - vWidth)
+ wx = tx + vWidth;
+ else if (m + w->width + w->output.right > vWidth)
+ wx = tx - vWidth;
+ else
+ wx = tx;
+
+ if (w->saveMask & CWX)
+ w->saveWc.x += wx;
+
+ moveWindow (w, wx, 0, sync);
+
+ if (sync)
+ syncWindowPosition (w);
+ }
+}
+
+CompGroup *
+addGroupToScreen (CompScreen *s,
+ Window id)
+{
+ CompGroup *group;
+
+ group = malloc (sizeof (CompGroup));
+ if (!group)
+ return NULL;
+
+ group->next = s->groups;
+ group->refCnt = 1;
+ group->id = id;
+
+ s->groups = group;
+
+ return group;
+}
+
+void
+removeGroupFromScreen (CompScreen *s,
+ CompGroup *group)
+{
+ group->refCnt--;
+ if (group->refCnt)
+ return;
+
+ if (group == s->groups)
+ {
+ s->groups = group->next;
+ }
+ else
+ {
+ CompGroup *g;
+
+ for (g = s->groups; g; g = g->next)
+ {
+ if (g->next == group)
+ {
+ g->next = group->next;
+ break;
+ }
+ }
+ }
+
+ free (group);
+}
+
+CompGroup *
+findGroupAtScreen (CompScreen *s,
+ Window id)
+{
+ CompGroup *g;
+
+ for (g = s->groups; g; g = g->next)
+ if (g->id == id)
+ return g;
+
+ return NULL;
+}
+
+void
+applyStartupProperties (CompScreen *screen,
+ CompWindow *window)
+{
+ CompStartupSequence *s = NULL;
+ const char *startupId = window->startupId;
+
+ printf ("Applying startup props to %d id \"%s\"\n",
+ (int) window->id, window->startupId ? window->startupId : "(none)");
+
+ if (!startupId)
+ {
+ const char *wmClass;
+
+ for (s = screen->startupSequences; s; s = s->next)
+ {
+ wmClass = sn_startup_sequence_get_wmclass (s->sequence);
+ if (!wmClass)
+ continue;
+
+ if ((window->resClass && strcmp (wmClass, window->resClass) == 0) ||
+ (window->resName && strcmp (wmClass, window->resName) == 0))
+ {
+ startupId = sn_startup_sequence_get_id (s->sequence);
+
+ window->startupId = strdup (startupId);
+
+ printf ("Ending legacy sequence %s due to window %d\n",
+ startupId, (int) window->id);
+
+ sn_startup_sequence_complete (s->sequence);
+ break;
+ }
+ }
+
+ if (!startupId)
+ return;
+ }
+
+ if (!s)
+ {
+ const char *id;
+
+ for (s = screen->startupSequences; s; s = s->next)
+ {
+ id = sn_startup_sequence_get_id (s->sequence);
+
+ if (strcmp (id, startupId) == 0)
+ break;
+ }
+ }
+
+ if (s)
+ {
+ printf ("Found startup sequence for window %d ID \"%s\"\n",
+ (int) window->id, startupId);
+ }
+ else
+ {
+ printf ("Did not find startup sequence for window %d ID \"%s\"\n",
+ (int) window->id, startupId);
+ }
+}
+
+void
+enterShowDesktopMode (CompScreen *s)
+{
+ CompWindow *w;
+ unsigned long data = 1;
+
+ s->showingDesktopMask = ~(CompWindowTypeDesktopMask |
+ CompWindowTypeDockMask);
+
+ for (w = s->windows; w; w = w->next)
+ hideWindow (w);
+
+ XChangeProperty (s->display->display, s->root,
+ s->display->showingDesktopAtom,
+ XA_CARDINAL, 32, PropModeReplace,
+ (unsigned char *) &data, 1);
+}
+
+void
+leaveShowDesktopMode (CompScreen *s)
+{
+ CompWindow *w;
+ unsigned long data = 0;
+
+ s->showingDesktopMask = 0;
+
+ for (w = s->windows; w; w = w->next)
+ showWindow (w);
+
+ XChangeProperty (s->display->display, s->root,
+ s->display->showingDesktopAtom,
+ XA_CARDINAL, 32, PropModeReplace,
+ (unsigned char *) &data, 1);
+}
+
+void
+sendWindowActivationRequest (CompScreen *s,
+ Window id)
+{
+ XEvent xev;
+
+ xev.xclient.type = ClientMessage;
+ xev.xclient.display = s->display->display;
+ xev.xclient.format = 32;
+
+ xev.xclient.message_type = s->display->winActiveAtom;
+ xev.xclient.window = id;
+
+ xev.xclient.data.l[0] = 2;
+ xev.xclient.data.l[1] = 0;
+ 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);
+}
diff --git a/src/texture.c b/src/texture.c
new file mode 100644
index 00000000..02e680a2
--- /dev/null
+++ b/src/texture.c
@@ -0,0 +1,337 @@
+/*
+ * 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 <compiz.h>
+
+static CompMatrix _identity_matrix = {
+ 1.0f, 0.0f,
+ 0.0f, 1.0f,
+ 0.0f, 0.0f
+};
+
+void
+initTexture (CompScreen *screen,
+ CompTexture *texture)
+{
+ texture->name = 0;
+ texture->target = GL_TEXTURE_2D;
+ texture->pixmap = None;
+ texture->filter = COMP_TEXTURE_FILTER_FAST;
+ texture->wrap = GL_CLAMP_TO_EDGE;
+ texture->matrix = _identity_matrix;
+}
+
+void
+finiTexture (CompScreen *screen,
+ CompTexture *texture)
+{
+ if (texture->name)
+ {
+ releasePixmapFromTexture (screen, texture);
+ glDeleteTextures (1, &texture->name);
+ }
+}
+
+static Bool
+imageToTexture (CompScreen *screen,
+ CompTexture *texture,
+ char *image,
+ unsigned int width,
+ unsigned int height)
+{
+ char *data;
+ int i;
+
+ data = malloc (4 * width * height);
+ if (!data)
+ return FALSE;
+
+ for (i = 0; i < height; i++)
+ memcpy (&data[i * width * 4],
+ &image[(height - i - 1) * width * 4],
+ width * 4);
+
+ releasePixmapFromTexture (screen, texture);
+
+ if (screen->textureNonPowerOfTwo ||
+ (POWER_OF_TWO (width) && POWER_OF_TWO (height)))
+ {
+ texture->target = GL_TEXTURE_2D;
+ texture->matrix.xx = 1.0f / width;
+ texture->matrix.yy = -1.0f / height;
+ texture->matrix.y0 = 1.0f;
+ }
+ else
+ {
+ texture->target = GL_TEXTURE_RECTANGLE_NV;
+ texture->matrix.xx = 1.0f;
+ texture->matrix.yy = -1.0f;
+ texture->matrix.y0 = height;
+ }
+
+ if (!texture->name)
+ glGenTextures (1, &texture->name);
+
+ glBindTexture (texture->target, texture->name);
+
+ glTexImage2D (texture->target, 0, GL_RGBA, width, height, 0, GL_BGRA,
+
+#if IMAGE_BYTE_ORDER == MSBFirst
+ GL_UNSIGNED_INT_8_8_8_8_REV,
+#else
+ GL_UNSIGNED_BYTE,
+#endif
+
+ data);
+
+ texture->filter = COMP_TEXTURE_FILTER_FAST;
+
+ glTexParameteri (texture->target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri (texture->target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+ glTexParameteri (texture->target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri (texture->target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ texture->wrap = GL_CLAMP_TO_EDGE;
+
+ glBindTexture (texture->target, 0);
+
+ free (data);
+
+ return TRUE;
+}
+
+Bool
+readImageToTexture (CompScreen *screen,
+ CompTexture *texture,
+ char *imageFileName,
+ unsigned int *returnWidth,
+ unsigned int *returnHeight)
+{
+ char *image;
+ unsigned int width, height;
+ Bool status;
+
+ if (!readPng (imageFileName, &image, &width, &height))
+ {
+ fprintf (stderr, "%s: Failed to load image: %s\n",
+ programName, imageFileName);
+ return FALSE;
+ }
+
+ status = imageToTexture (screen, texture, image, width, height);
+
+ free (image);
+
+ *returnWidth = width;
+ *returnHeight = height;
+
+ return status;
+}
+
+Bool
+readImageBufferToTexture (CompScreen *screen,
+ CompTexture *texture,
+ const unsigned char *imageBuffer,
+ unsigned int *returnWidth,
+ unsigned int *returnHeight)
+{
+ char *image;
+ unsigned int width, height;
+ Bool status;
+
+ if (!readPngBuffer (imageBuffer, &image, &width, &height))
+ {
+ fprintf (stderr, "%s: Failed to load image buffer\n", programName);
+ return FALSE;
+ }
+
+ status = imageToTexture (screen, texture, image, width, height);
+
+ free (image);
+
+ *returnWidth = width;
+ *returnHeight = height;
+
+ return status;
+}
+
+Bool
+bindPixmapToTexture (CompScreen *screen,
+ CompTexture *texture,
+ Pixmap pixmap,
+ int width,
+ int height,
+ int depth)
+{
+ XVisualInfo *visinfo;
+ unsigned int target;
+
+ visinfo = screen->glxPixmapVisuals[depth];
+ if (!visinfo)
+ {
+ fprintf (stderr, "%s: No GL visual for depth %d\n",
+ programName, depth);
+
+ return FALSE;
+ }
+
+ texture->pixmap = glXCreateGLXPixmap (screen->display->display,
+ visinfo, pixmap);
+ if (!texture->pixmap)
+ {
+ fprintf (stderr, "%s: glXCreateGLXPixmap failed\n", programName);
+
+ return FALSE;
+ }
+
+ screen->queryDrawable (screen->display->display,
+ texture->pixmap,
+ GLX_TEXTURE_TARGET_EXT,
+ &target);
+ switch (target) {
+ case GLX_TEXTURE_2D_EXT:
+ texture->target = GL_TEXTURE_2D;
+ texture->matrix.xx = 1.0f / width;
+ texture->matrix.yy = -1.0f / height;
+ texture->matrix.y0 = 1.0f;
+ break;
+ case GLX_TEXTURE_RECTANGLE_EXT:
+ texture->target = GL_TEXTURE_RECTANGLE_ARB;
+ texture->matrix.xx = 1.0f;
+ texture->matrix.yy = -1.0f;
+ texture->matrix.y0 = height;
+ break;
+ case GLX_NO_TEXTURE_EXT:
+ fprintf (stderr, "%s: pixmap 0x%x can't be bound to texture\n",
+ programName, (int) pixmap);
+
+ /* fall-through */
+ default:
+ glXDestroyGLXPixmap (screen->display->display, texture->pixmap);
+ texture->pixmap = None;
+
+ return FALSE;
+ }
+
+ if (!texture->name)
+ glGenTextures (1, &texture->name);
+
+ glBindTexture (texture->target, texture->name);
+
+ if (!screen->bindTexImage (screen->display->display,
+ texture->pixmap,
+ GLX_FRONT_LEFT_EXT))
+ {
+ fprintf (stderr, "%s: glXBindTexImage failed\n", programName);
+
+ glXDestroyGLXPixmap (screen->display->display, texture->pixmap);
+ texture->pixmap = None;
+
+ return FALSE;
+ }
+
+ texture->filter = COMP_TEXTURE_FILTER_FAST;
+
+ glTexParameteri (texture->target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri (texture->target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+ glTexParameteri (texture->target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri (texture->target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ texture->wrap = GL_CLAMP_TO_EDGE;
+
+ glBindTexture (texture->target, 0);
+
+ return TRUE;
+}
+
+void
+releasePixmapFromTexture (CompScreen *screen,
+ CompTexture *texture)
+{
+ if (texture->pixmap)
+ {
+ if (!testMode)
+ {
+ glEnable (texture->target);
+ glBindTexture (texture->target, texture->name);
+
+ screen->releaseTexImage (screen->display->display,
+ texture->pixmap,
+ GLX_FRONT_LEFT_EXT);
+
+ glBindTexture (texture->target, 0);
+ glDisable (texture->target);
+
+ glXDestroyGLXPixmap (screen->display->display, texture->pixmap);
+ }
+ texture->pixmap = None;
+ }
+}
+
+void
+enableTexture (CompScreen *screen,
+ CompTexture *texture,
+ CompTextureFilter filter)
+{
+ glEnable (texture->target);
+ glBindTexture (texture->target, texture->name);
+
+ if (filter != texture->filter)
+ {
+ switch (filter) {
+ case COMP_TEXTURE_FILTER_FAST:
+ glTexParameteri (texture->target,
+ GL_TEXTURE_MIN_FILTER,
+ GL_NEAREST);
+ glTexParameteri (texture->target,
+ GL_TEXTURE_MAG_FILTER,
+ GL_NEAREST);
+ break;
+ case COMP_TEXTURE_FILTER_GOOD:
+ glTexParameteri (texture->target,
+ GL_TEXTURE_MIN_FILTER,
+ screen->display->textureFilter);
+ glTexParameteri (texture->target,
+ GL_TEXTURE_MAG_FILTER,
+ screen->display->textureFilter);
+ break;
+ }
+
+ texture->filter = filter;
+ }
+}
+
+void
+disableTexture (CompTexture *texture)
+{
+ glBindTexture (texture->target, 0);
+ glDisable (texture->target);
+}
diff --git a/src/window.c b/src/window.c
new file mode 100644
index 00000000..fc19390d
--- /dev/null
+++ b/src/window.c
@@ -0,0 +1,2903 @@
+/*
+ * 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/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/Xproto.h>
+#include <X11/extensions/shape.h>
+#include <X11/extensions/Xcomposite.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include <compiz.h>
+
+#define MwmHintsDecorations (1L << 1)
+
+#define PropMotifWmHintElements 3
+
+typedef struct {
+ unsigned long flags;
+ unsigned long functions;
+ unsigned long decorations;
+} MwmHints;
+
+static int
+reallocWindowPrivates (int size,
+ void *closure)
+{
+ CompScreen *s = (CompScreen *) closure;
+ CompWindow *w;
+ void *privates;
+
+ for (w = s->windows; w; w = w->next)
+ {
+ privates = realloc (w->privates, size * sizeof (CompPrivate));
+ if (!privates)
+ return FALSE;
+
+ w->privates = (CompPrivate *) privates;
+ }
+
+ return TRUE;
+}
+
+int
+allocateWindowPrivateIndex (CompScreen *screen)
+{
+ return allocatePrivateIndex (&screen->windowPrivateLen,
+ &screen->windowPrivateIndices,
+ reallocWindowPrivates,
+ (void *) screen);
+}
+
+void
+freeWindowPrivateIndex (CompScreen *screen,
+ int index)
+{
+ freePrivateIndex (screen->windowPrivateLen,
+ screen->windowPrivateIndices,
+ index);
+}
+
+static void
+recalcNormalHints (CompWindow *window)
+{
+ window->sizeHints.x = window->attrib.x;
+ window->sizeHints.y = window->attrib.y;
+ window->sizeHints.width = window->attrib.width;
+ window->sizeHints.height = window->attrib.height;
+
+ if (window->sizeHints.flags & PMinSize)
+ {
+ window->sizeHints.base_width = window->sizeHints.min_width;
+ window->sizeHints.base_height = window->sizeHints.min_height;
+ }
+ else
+ {
+ window->sizeHints.base_width = 0;
+ window->sizeHints.base_height = 0;
+ }
+
+ window->sizeHints.flags |= PBaseSize;
+
+ if (window->sizeHints.flags & PBaseSize)
+ {
+ window->sizeHints.min_width = window->sizeHints.base_width;
+ window->sizeHints.min_height = window->sizeHints.base_height;
+ }
+ else
+ {
+ window->sizeHints.min_width = 0;
+ window->sizeHints.min_height = 0;
+ }
+ window->sizeHints.flags |= PMinSize;
+
+ if (!(window->sizeHints.flags & PMaxSize))
+ {
+ window->sizeHints.max_width = 65535;
+ window->sizeHints.max_height = 65535;
+ window->sizeHints.flags |= PMaxSize;
+ }
+
+ if (window->sizeHints.max_width < window->sizeHints.min_width)
+ window->sizeHints.max_width = window->sizeHints.min_width;
+
+ if (window->sizeHints.max_height < window->sizeHints.min_height)
+ window->sizeHints.max_height = window->sizeHints.min_height;
+
+ if (window->sizeHints.min_width < 1)
+ window->sizeHints.min_width = 1;
+
+ if (window->sizeHints.max_width < 1)
+ window->sizeHints.max_width = 1;
+
+ if (window->sizeHints.min_height < 1)
+ window->sizeHints.min_height = 1;
+
+ if (window->sizeHints.max_height < 1)
+ window->sizeHints.max_height = 1;
+
+ if (window->sizeHints.flags & PResizeInc)
+ {
+ if (window->sizeHints.width_inc == 0)
+ window->sizeHints.width_inc = 1;
+
+ if (window->sizeHints.height_inc == 0)
+ window->sizeHints.height_inc = 1;
+ }
+ else
+ {
+ window->sizeHints.width_inc = 1;
+ window->sizeHints.height_inc = 1;
+ window->sizeHints.flags |= PResizeInc;
+ }
+
+ if (window->sizeHints.flags & PAspect)
+ {
+ /* don't divide by 0 */
+ if (window->sizeHints.min_aspect.y < 1)
+ window->sizeHints.min_aspect.y = 1;
+
+ if (window->sizeHints.max_aspect.y < 1)
+ window->sizeHints.max_aspect.y = 1;
+ }
+ else
+ {
+ window->sizeHints.min_aspect.x = 1;
+ window->sizeHints.min_aspect.y = 65535;
+ window->sizeHints.max_aspect.x = 65535;
+ window->sizeHints.max_aspect.y = 1;
+ window->sizeHints.flags |= PAspect;
+ }
+
+ if (!(window->sizeHints.flags & PWinGravity))
+ {
+ window->sizeHints.win_gravity = NorthWestGravity;
+ window->sizeHints.flags |= PWinGravity;
+ }
+}
+
+void
+updateNormalHints (CompWindow *w)
+{
+ Status status;
+ long supplied;
+
+ status = XGetWMNormalHints (w->screen->display->display, w->id,
+ &w->sizeHints, &supplied);
+
+ if (!status)
+ w->sizeHints.flags = 0;
+
+ recalcNormalHints (w);
+}
+
+void
+updateWmHints (CompWindow *w)
+{
+ XWMHints *hints;
+
+ hints = XGetWMHints (w->screen->display->display, w->id);
+ if (hints)
+ {
+ if (hints->flags & InputHint)
+ w->inputHint = hints->input;
+
+ XFree (hints);
+ }
+}
+
+void
+updateWindowClassHints (CompWindow *w)
+{
+ XClassHint classHint;
+ int status;
+
+ status = XGetClassHint (w->screen->display->display, w->id, &classHint);
+
+ if (status)
+ {
+ if (classHint.res_name)
+ {
+ if (w->resName)
+ free (w->resName);
+
+ w->resName = strdup (classHint.res_name);
+ XFree (classHint.res_name);
+ }
+
+ if (classHint.res_class)
+ {
+ if (w->resClass)
+ free (w->resClass);
+
+ w->resClass = strdup (classHint.res_class);
+ XFree (classHint.res_class);
+ }
+ }
+}
+
+static Window
+getClientLeaderOfAncestor (CompWindow *w)
+{
+ if (w->transientFor)
+ {
+ w = findWindowAtScreen (w->screen, w->transientFor);
+ if (w)
+ {
+ if (w->clientLeader)
+ return w->clientLeader;
+
+ return getClientLeaderOfAncestor (w);
+ }
+ }
+
+ return None;
+}
+
+Window
+getClientLeader (CompWindow *w)
+{
+ Atom actual;
+ int result, format;
+ unsigned long n, left;
+ unsigned char *data;
+
+ result = XGetWindowProperty (w->screen->display->display, w->id,
+ w->screen->display->wmClientLeaderAtom,
+ 0L, 1L, False, XA_WINDOW, &actual, &format,
+ &n, &left, &data);
+
+ if (result == Success && n && data)
+ {
+ Window win;
+
+ memcpy (&win, data, sizeof (Window));
+ XFree ((void *) data);
+
+ if (win)
+ return win;
+ }
+
+ return getClientLeaderOfAncestor (w);
+}
+
+int
+getWmState (CompDisplay *display,
+ Window id)
+{
+ Atom actual;
+ int result, format;
+ unsigned long n, left;
+ unsigned char *data;
+ unsigned long state = NormalState;
+
+ result = XGetWindowProperty (display->display, id,
+ display->wmStateAtom, 0L, 2L, FALSE,
+ display->wmStateAtom, &actual, &format,
+ &n, &left, &data);
+
+ if (result == Success && n && data)
+ {
+ memcpy (&state, data, sizeof (unsigned long));
+ XFree ((void *) data);
+ }
+
+ return state;
+}
+
+void
+setWmState (CompDisplay *display,
+ int state,
+ Window id)
+{
+ unsigned long data[2];
+
+ data[0] = state;
+ data[1] = None;
+
+ XChangeProperty (display->display, id,
+ display->wmStateAtom, display->wmStateAtom,
+ 32, PropModeReplace, (unsigned char *) data, 2);
+}
+
+unsigned int
+windowStateMask (CompDisplay *display,
+ Atom state)
+{
+ if (state == display->winStateModalAtom)
+ return CompWindowStateModalMask;
+ else if (state == display->winStateStickyAtom)
+ return CompWindowStateStickyMask;
+ else if (state == display->winStateMaximizedVertAtom)
+ return CompWindowStateMaximizedVertMask;
+ else if (state == display->winStateMaximizedHorzAtom)
+ return CompWindowStateMaximizedHorzMask;
+ else if (state == display->winStateShadedAtom)
+ return CompWindowStateShadedMask;
+ else if (state == display->winStateSkipTaskbarAtom)
+ return CompWindowStateSkipTaskbarMask;
+ else if (state == display->winStateSkipPagerAtom)
+ return CompWindowStateSkipPagerMask;
+ else if (state == display->winStateHiddenAtom)
+ return CompWindowStateHiddenMask;
+ else if (state == display->winStateFullscreenAtom)
+ return CompWindowStateFullscreenMask;
+ else if (state == display->winStateAboveAtom)
+ return CompWindowStateAboveMask;
+ else if (state == display->winStateBelowAtom)
+ return CompWindowStateBelowMask;
+ else if (state == display->winStateDemandsAttentionAtom)
+ return CompWindowStateDemandsAttentationMask;
+ else if (state == display->winStateDisplayModalAtom)
+ return CompWindowStateDisplayModalMask;
+
+ return 0;
+}
+
+unsigned int
+getWindowState (CompDisplay *display,
+ Window id)
+{
+ Atom actual;
+ int result, format;
+ unsigned long n, left;
+ unsigned char *data;
+ unsigned int state = 0;
+
+ result = XGetWindowProperty (display->display, id, display->winStateAtom,
+ 0L, 1024L, FALSE, XA_ATOM, &actual, &format,
+ &n, &left, &data);
+
+ if (result == Success && n && data)
+ {
+ Atom *a = (Atom *) data;
+
+ while (n--)
+ state |= windowStateMask (display, *a++);
+
+ XFree ((void *) data);
+ }
+
+ return state;
+}
+
+void
+setWindowState (CompDisplay *display,
+ unsigned int state,
+ Window id)
+{
+ Atom data[32];
+ int i = 0;
+
+ if (state & CompWindowStateModalMask)
+ data[i++] = display->winStateModalAtom;
+ if (state & CompWindowStateStickyMask)
+ data[i++] = display->winStateStickyAtom;
+ if (state & CompWindowStateMaximizedVertMask)
+ data[i++] = display->winStateMaximizedVertAtom;
+ if (state & CompWindowStateMaximizedHorzMask)
+ data[i++] = display->winStateMaximizedHorzAtom;
+ if (state & CompWindowStateShadedMask)
+ data[i++] = display->winStateShadedAtom;
+ if (state & CompWindowStateSkipTaskbarMask)
+ data[i++] = display->winStateSkipTaskbarAtom;
+ if (state & CompWindowStateSkipPagerMask)
+ data[i++] = display->winStateSkipPagerAtom;
+ if (state & CompWindowStateHiddenMask)
+ data[i++] = display->winStateHiddenAtom;
+ if (state & CompWindowStateFullscreenMask)
+ data[i++] = display->winStateFullscreenAtom;
+ if (state & CompWindowStateAboveMask)
+ data[i++] = display->winStateAboveAtom;
+ if (state & CompWindowStateBelowMask)
+ data[i++] = display->winStateBelowAtom;
+ if (state & CompWindowStateDemandsAttentationMask)
+ data[i++] = display->winStateDemandsAttentionAtom;
+ if (state & CompWindowStateDisplayModalMask)
+ data[i++] = display->winStateDisplayModalAtom;
+
+ XChangeProperty (display->display, id, display->winStateAtom,
+ XA_ATOM, 32, PropModeReplace,
+ (unsigned char *) data, i);
+}
+
+static void
+setWindowActions (CompDisplay *display,
+ unsigned int actions,
+ Window id)
+{
+ Atom data[32];
+ int i = 0;
+
+ if (actions & CompWindowActionMoveMask)
+ data[i++] = display->winActionMoveAtom;
+ if (actions & CompWindowActionResizeMask)
+ data[i++] = display->winActionResizeAtom;
+ if (actions & CompWindowActionStickMask)
+ data[i++] = display->winActionStickAtom;
+ if (actions & CompWindowActionMinimizeMask)
+ data[i++] = display->winActionMinimizeAtom;
+ if (actions & CompWindowActionMaximizeHorzMask)
+ data[i++] = display->winActionMaximizeHorzAtom;
+ if (actions & CompWindowActionMaximizeVertMask)
+ data[i++] = display->winActionMaximizeVertAtom;
+ if (actions & CompWindowActionFullscreenMask)
+ data[i++] = display->winActionFullscreenAtom;
+ if (actions & CompWindowActionCloseMask)
+ data[i++] = display->winActionCloseAtom;
+
+ XChangeProperty (display->display, id, display->wmAllowedActionsAtom,
+ XA_ATOM, 32, PropModeReplace,
+ (unsigned char *) data, i);
+}
+
+static void
+recalcWindowActions (CompWindow *w)
+{
+ unsigned int actions = 0;
+
+ switch (w->type) {
+ case CompWindowTypeFullscreenMask:
+ case CompWindowTypeNormalMask:
+ actions |=
+ CompWindowActionMinimizeMask |
+ CompWindowActionMaximizeHorzMask |
+ CompWindowActionMaximizeVertMask |
+ CompWindowActionFullscreenMask;
+ /* fall-through */
+ case CompWindowTypeDialogMask:
+ case CompWindowTypeModalDialogMask:
+ actions |=
+ CompWindowActionMoveMask |
+ CompWindowActionResizeMask |
+ CompWindowActionStickMask |
+ CompWindowActionCloseMask;
+ break;
+ case CompWindowTypeUtilMask:
+ case CompWindowTypeToolbarMask:
+ case CompWindowTypeMenuMask:
+ case CompWindowTypeSplashMask:
+ case CompWindowTypeDesktopMask:
+ case CompWindowTypeDockMask:
+ case CompWindowTypeUnknownMask:
+ default:
+ break;
+ }
+
+ if (actions != w->actions)
+ {
+ w->actions = actions;
+ setWindowActions (w->screen->display, actions, w->id);
+ }
+}
+
+unsigned int
+getWindowType (CompDisplay *display,
+ Window id)
+{
+ Atom actual;
+ int result, format;
+ unsigned long n, left;
+ unsigned char *data;
+
+ result = XGetWindowProperty (display->display, id, display->winTypeAtom,
+ 0L, 1L, FALSE, XA_ATOM, &actual, &format,
+ &n, &left, &data);
+
+ if (result == Success && n && data)
+ {
+ Atom a;
+
+ memcpy (&a, data, sizeof (Atom));
+ XFree ((void *) data);
+
+ if (a == display->winTypeNormalAtom)
+ return CompWindowTypeNormalMask;
+ else if (a == display->winTypeMenuAtom)
+ return CompWindowTypeMenuMask;
+ else if (a == display->winTypeDesktopAtom)
+ return CompWindowTypeDesktopMask;
+ else if (a == display->winTypeDockAtom)
+ return CompWindowTypeDockMask;
+ else if (a == display->winTypeToolbarAtom)
+ return CompWindowTypeToolbarMask;
+ else if (a == display->winTypeUtilAtom)
+ return CompWindowTypeUtilMask;
+ else if (a == display->winTypeSplashAtom)
+ return CompWindowTypeSplashMask;
+ else if (a == display->winTypeDialogAtom)
+ return CompWindowTypeDialogMask;
+ }
+
+ return CompWindowTypeUnknownMask;
+}
+
+void
+recalcWindowType (CompWindow *w)
+{
+ unsigned int type;
+
+ type = w->wmType;
+
+ if (!w->attrib.override_redirect && w->wmType == CompWindowTypeUnknownMask)
+ type = CompWindowTypeNormalMask;
+
+ if (w->state & CompWindowStateFullscreenMask)
+ type = CompWindowTypeFullscreenMask;
+
+ if (type == CompWindowTypeNormalMask)
+ {
+ if (w->transientFor)
+ type = CompWindowTypeDialogMask;
+ }
+
+ if ((type & (CompWindowTypeNormalMask | CompWindowTypeDialogMask)) &&
+ (w->state & CompWindowStateModalMask))
+ type = CompWindowTypeModalDialogMask;
+
+ if (type != w->type)
+ {
+ w->type = type;
+ recalcWindowActions (w);
+ }
+}
+
+unsigned int
+getMwmDecor (CompDisplay *display,
+ Window id)
+{
+ Atom actual;
+ int result, format;
+ unsigned long n, left;
+ MwmHints *mwmHints;
+ unsigned int decor = MwmDecorAll;
+
+ result = XGetWindowProperty (display->display, id, display->mwmHintsAtom,
+ 0L, 20L, FALSE, display->mwmHintsAtom,
+ &actual, &format, &n, &left,
+ (unsigned char **) &mwmHints);
+
+ if (result == Success && n && mwmHints)
+ {
+ if (n >= PropMotifWmHintElements)
+ {
+ if (mwmHints->flags & MwmHintsDecorations)
+ decor = mwmHints->decorations;
+ }
+
+ XFree (mwmHints);
+ }
+
+ return decor;
+}
+
+unsigned int
+getProtocols (CompDisplay *display,
+ Window id)
+{
+ Atom actual;
+ int result, format;
+ unsigned long n, left;
+ Atom *protocol;
+ unsigned int protocols = 0;
+
+ result = XGetWindowProperty (display->display, id, display->wmProtocolsAtom,
+ 0L, 20L, FALSE, XA_ATOM,
+ &actual, &format, &n, &left,
+ (unsigned char **) &protocol);
+
+ if (result == Success && n && protocol)
+ {
+ int i;
+
+ for (i = 0; i < n; i++)
+ {
+ if (protocol[i] == display->wmDeleteWindowAtom)
+ protocols |= CompWindowProtocolDeleteMask;
+ else if (protocol[i] == display->wmTakeFocusAtom)
+ protocols |= CompWindowProtocolTakeFocusMask;
+ else if (protocol[i] == display->wmPingAtom)
+ protocols |= CompWindowProtocolPingMask;
+ else if (protocol[i] == display->wmSyncRequestAtom)
+ protocols |= CompWindowProtocolSyncRequestMask;
+ }
+
+ XFree (protocol);
+ }
+
+ return protocols;
+}
+
+unsigned short
+getWindowProp32 (CompDisplay *display,
+ Window id,
+ Atom property,
+ unsigned short defaultValue)
+{
+ Atom actual;
+ int result, format;
+ unsigned long n, left;
+ unsigned char *data;
+
+ result = XGetWindowProperty (display->display, id, property,
+ 0L, 1L, FALSE, XA_CARDINAL, &actual, &format,
+ &n, &left, &data);
+
+ if (result == Success && n && data)
+ {
+ CARD32 value;
+
+ memcpy (&value, data, sizeof (CARD32));
+
+ XFree (data);
+
+ return value >> 16;
+ }
+
+ return defaultValue;
+}
+
+void
+setWindowProp32 (CompDisplay *display,
+ Window id,
+ Atom property,
+ unsigned short value)
+{
+ CARD32 value32;
+
+ value32 = value << 16 | value;
+
+ XChangeProperty (display->display, id, property,
+ XA_CARDINAL, 32, PropModeReplace,
+ (unsigned char *) &value32, 1);
+}
+
+
+static void
+updateFrameWindow (CompWindow *w)
+{
+ if (w->input.left || w->input.right || w->input.top || w->input.bottom)
+ {
+ XRectangle rects[4];
+ int x, y, width, height;
+
+ x = w->serverX - w->input.left;
+ y = w->serverY - w->input.top;
+ width = w->width + w->input.left + w->input.right;
+ height = w->height + w->input.top + w->input.bottom;
+
+ if (!w->frame)
+ {
+ XSetWindowAttributes attr;
+ XWindowChanges xwc;
+
+ attr.event_mask = 0;
+ attr.override_redirect = TRUE;
+
+ w->frame = XCreateWindow (w->screen->display->display,
+ w->screen->root,
+ x, y, width, height, 0,
+ CopyFromParent,
+ InputOnly,
+ CopyFromParent,
+ CWOverrideRedirect | CWEventMask, &attr);
+
+ XGrabButton (w->screen->display->display, AnyButton,
+ AnyModifier, w->frame, TRUE, ButtonPressMask |
+ ButtonReleaseMask | ButtonMotionMask,
+ GrabModeSync, GrabModeSync, None, None);
+
+ xwc.stack_mode = Below;
+ xwc.sibling = w->id;
+
+ XConfigureWindow (w->screen->display->display, w->frame,
+ CWSibling | CWStackMode, &xwc);
+
+ if (w->mapNum)
+ XMapWindow (w->screen->display->display, w->frame);
+
+ XChangeProperty (w->screen->display->display, w->id,
+ w->screen->display->frameWindowAtom,
+ XA_WINDOW, 32, PropModeReplace,
+ (unsigned char *) &w->frame, 1);
+ }
+
+ XResizeWindow (w->screen->display->display, w->frame, width, height);
+
+ rects[0].x = 0;
+ rects[0].y = 0;
+ rects[0].width = width;
+ rects[0].height = w->input.top;
+
+ rects[1].x = 0;
+ rects[1].y = w->input.top;
+ rects[1].width = w->input.left;
+ rects[1].height = height - w->input.top - w->input.bottom;
+
+ rects[2].x = width - w->input.right;
+ rects[2].y = w->input.top;
+ rects[2].width = w->input.right;
+ rects[2].height = height - w->input.top - w->input.bottom;
+
+ rects[3].x = 0;
+ rects[3].y = height - w->input.bottom;
+ rects[3].width = width;
+ rects[3].height = w->input.bottom;
+
+ XShapeCombineRectangles (w->screen->display->display,
+ w->frame,
+ ShapeInput,
+ 0,
+ 0,
+ rects,
+ 4,
+ ShapeSet,
+ YXBanded);
+ }
+ else
+ {
+ if (w->frame)
+ {
+ XDestroyWindow (w->screen->display->display, w->frame);
+ w->frame = None;
+ }
+ }
+}
+
+void
+setWindowFrameExtents (CompWindow *w,
+ CompWindowExtents *input,
+ CompWindowExtents *output)
+{
+ if (input->left != w->input.left ||
+ input->right != w->input.right ||
+ input->top != w->input.top ||
+ input->bottom != w->input.bottom)
+ {
+ unsigned long data[4];
+
+ w->input = *input;
+
+ data[0] = input->left;
+ data[1] = input->right;
+ data[2] = input->top;
+ data[3] = input->bottom;
+
+ updateFrameWindow (w);
+
+ XChangeProperty (w->screen->display->display, w->id,
+ w->screen->display->frameExtentsAtom,
+ XA_CARDINAL, 32, PropModeReplace,
+ (unsigned char *) data, 4);
+ }
+
+ if (output->left != w->output.left ||
+ output->right != w->output.right ||
+ output->top != w->output.top ||
+ output->bottom != w->output.bottom)
+ {
+ w->output = *output;
+
+ (*w->screen->windowResizeNotify) (w);
+ }
+}
+
+static void
+setWindowMatrix (CompWindow *w)
+{
+ w->matrix = w->texture.matrix;
+ w->matrix.x0 -= (w->attrib.x * w->matrix.xx);
+ w->matrix.y0 -= (w->attrib.y * w->matrix.yy);
+}
+
+void
+bindWindow (CompWindow *w)
+{
+ if (testMode)
+ {
+ unsigned int width, height;
+
+ if (readImageToTexture (w->screen, &w->texture,
+ windowImage, &width, &height))
+ {
+ XResizeWindow (w->screen->display->display, w->id, width, height);
+
+ w->width = width;
+ w->height = height;
+ }
+
+ w->pixmap = 1;
+ w->texture.pixmap = 1;
+ }
+ else
+ {
+ if (!w->pixmap)
+ {
+ w->pixmap =
+ XCompositeNameWindowPixmap (w->screen->display->display,
+ w->id);
+ if (!w->pixmap)
+ {
+ fprintf (stderr, "%s: XCompositeNameWindowPixmap failed\n",
+ programName);
+ }
+ }
+
+ if (!bindPixmapToTexture (w->screen, &w->texture, w->pixmap,
+ w->width, w->height,
+ w->attrib.depth))
+ {
+ fprintf (stderr, "%s: Couldn't bind redirected window 0x%x to "
+ "texture\n", programName, (int) w->id);
+ }
+ }
+
+ setWindowMatrix (w);
+}
+
+void
+releaseWindow (CompWindow *w)
+{
+ if (w->pixmap)
+ {
+ releasePixmapFromTexture (w->screen, &w->texture);
+
+ if (!testMode)
+ XFreePixmap (w->screen->display->display, w->pixmap);
+
+ w->pixmap = None;
+ }
+}
+
+static void
+freeWindow (CompWindow *w)
+{
+ releaseWindow (w);
+
+ if (w->syncAlarm)
+ XSyncDestroyAlarm (w->screen->display->display, w->syncAlarm);
+
+ if (w->syncWaitHandle)
+ compRemoveTimeout (w->syncWaitHandle);
+
+ if (w->texture.name)
+ finiTexture (w->screen, &w->texture);
+
+ if (w->clip)
+ XDestroyRegion (w->clip);
+
+ if (w->region)
+ XDestroyRegion (w->region);
+
+ if (w->privates)
+ free (w->privates);
+
+ if (w->sizeDamage)
+ free (w->damageRects);
+
+ if (w->vertices)
+ free (w->vertices);
+
+ if (w->indices)
+ free (w->indices);
+
+ if (lastFoundWindow == w)
+ lastFoundWindow = 0;
+
+ if (lastDamagedWindow == w)
+ lastDamagedWindow = 0;
+
+ if (w->struts)
+ free (w->struts);
+
+ if (w->startupId)
+ free (w->startupId);
+
+ if (w->resName)
+ free (w->resName);
+
+ if (w->resClass)
+ free (w->resClass);
+
+ free (w);
+}
+
+void
+damageWindowRegion (CompWindow *w,
+ Region region)
+{
+ if (w->scaled)
+ {
+ REGION reg;
+ int x1, y1, x2, y2;
+
+ reg.rects = &reg.extents;
+ reg.numRects = 1;
+
+ x1 = region->extents.x1 - w->attrib.x;
+ y1 = region->extents.y1 - w->attrib.y;
+ x2 = region->extents.x2 - w->attrib.x;
+ y2 = region->extents.y2 - w->attrib.y;
+
+ reg.extents.x1 = (x1 * w->paint.xScale) + w->attrib.x;
+ reg.extents.y1 = (y1 * w->paint.yScale) + w->attrib.y;
+ reg.extents.x2 = (x2 * w->paint.xScale + 0.5f) + w->attrib.x;
+ reg.extents.y2 = (y2 * w->paint.yScale + 0.5f) + w->attrib.y;
+
+ if (reg.extents.x2 > reg.extents.x1 && reg.extents.y2 > reg.extents.y1)
+ damageScreenRegion (w->screen, &reg);
+ }
+ else
+ {
+ damageScreenRegion (w->screen, region);
+ }
+}
+
+void
+damageWindowOutputExtents (CompWindow *w)
+{
+ if (w->screen->damageMask & COMP_SCREEN_DAMAGE_ALL_MASK)
+ return;
+
+ if (w->attrib.map_state == IsViewable && w->damaged)
+ {
+ REGION reg;
+
+ reg.rects = &reg.extents;
+ reg.numRects = reg.size = 1;
+
+ /* top */
+ reg.extents.x1 = w->attrib.x - w->output.left;
+ reg.extents.y1 = w->attrib.y - w->output.top;
+ reg.extents.x2 = w->attrib.x + w->width + w->output.right;
+ reg.extents.y2 = w->attrib.y;
+
+ if (reg.extents.x1 < reg.extents.x2 && reg.extents.y1 < reg.extents.y2)
+ damageWindowRegion (w, &reg);
+
+ /* bottom */
+ reg.extents.y1 = w->attrib.y + w->height;
+ reg.extents.y2 = reg.extents.y1 + w->output.bottom;
+
+ if (reg.extents.x1 < reg.extents.x2 && reg.extents.y1 < reg.extents.y2)
+ damageWindowRegion (w, &reg);
+
+ /* left */
+ reg.extents.x1 = w->attrib.x - w->output.left;
+ reg.extents.y1 = w->attrib.y;
+ reg.extents.x2 = w->attrib.x;
+ reg.extents.y2 = w->attrib.y + w->height;
+
+ if (reg.extents.x1 < reg.extents.x2 && reg.extents.y1 < reg.extents.y2)
+ damageWindowRegion (w, &reg);
+
+ /* right */
+ reg.extents.x1 = w->attrib.x + w->width;
+ reg.extents.x2 = reg.extents.x1 + w->output.right;
+
+ if (reg.extents.x1 < reg.extents.x2 && reg.extents.y1 < reg.extents.y2)
+ damageWindowRegion (w, &reg);
+ }
+}
+
+Bool
+damageWindowRect (CompWindow *w,
+ Bool initial,
+ BoxPtr rect)
+{
+ return FALSE;
+}
+
+void
+addWindowDamage (CompWindow *w)
+{
+ if (w->screen->damageMask & COMP_SCREEN_DAMAGE_ALL_MASK)
+ return;
+
+ if (w->attrib.map_state == IsViewable && w->damaged)
+ {
+ REGION region;
+
+ region.extents.x1 = -w->output.left - w->attrib.border_width;
+ region.extents.y1 = -w->output.top - w->attrib.border_width;
+ region.extents.x2 = w->width + w->output.right;
+ region.extents.y2 = w->height + w->output.bottom;
+
+ if (!(*w->screen->damageWindowRect) (w, FALSE, &region.extents))
+ {
+ region.extents.x1 += w->attrib.x + w->attrib.border_width;
+ region.extents.y1 += w->attrib.y + w->attrib.border_width;
+ region.extents.x2 += w->attrib.x + w->attrib.border_width;
+ region.extents.y2 += w->attrib.y + w->attrib.border_width;
+
+ region.rects = &region.extents;
+ region.numRects = region.size = 1;
+
+ damageWindowRegion (w, &region);
+ }
+ }
+}
+
+void
+updateWindowRegion (CompWindow *w)
+{
+ REGION rect;
+ XRectangle r, *rects, *shapeRects = 0;
+ int i, n = 0;
+
+ EMPTY_REGION (w->region);
+
+ if (w->screen->display->shapeExtension)
+ {
+ int order;
+
+ shapeRects = XShapeGetRectangles (w->screen->display->display, w->id,
+ ShapeBounding, &n, &order);
+ }
+
+ if (n < 2)
+ {
+ r.x = -w->attrib.border_width;
+ r.y = -w->attrib.border_width;
+ r.width = w->width;
+ r.height = w->height;
+
+ rects = &r;
+ n = 1;
+ }
+ else
+ {
+ rects = shapeRects;
+ }
+
+ rect.rects = &rect.extents;
+ rect.numRects = rect.size = 1;
+
+ for (i = 0; i < n; i++)
+ {
+ rect.extents.x1 = rects[i].x + w->attrib.border_width;
+ rect.extents.y1 = rects[i].y + w->attrib.border_width;
+ rect.extents.x2 = rect.extents.x1 + rects[i].width;
+ rect.extents.y2 = rect.extents.y1 + rects[i].height;
+
+ if (rect.extents.x1 < 0)
+ rect.extents.x1 = 0;
+ if (rect.extents.y1 < 0)
+ rect.extents.y1 = 0;
+ if (rect.extents.x2 > w->width)
+ rect.extents.x2 = w->width;
+ if (rect.extents.y2 > w->height)
+ rect.extents.y2 = w->height;
+
+ if (rect.extents.y1 < rect.extents.y2 &&
+ rect.extents.x1 < rect.extents.x2)
+ {
+ rect.extents.x1 += w->attrib.x;
+ rect.extents.y1 += w->attrib.y;
+ rect.extents.x2 += w->attrib.x;
+ rect.extents.y2 += w->attrib.y;
+
+ XUnionRegion (&rect, w->region, w->region);
+ }
+ }
+
+ if (shapeRects)
+ XFree (shapeRects);
+}
+
+Bool
+updateWindowStruts (CompWindow *w)
+{
+ Atom actual;
+ int result, format;
+ unsigned long n, left;
+ unsigned long *struts = NULL;
+ Bool hasOld, hasNew;
+ CompStruts old, new;
+
+#define MIN_EMPTY 76
+
+ if (w->struts)
+ {
+ hasOld = TRUE;
+
+ old.left = w->struts->left;
+ old.right = w->struts->right;
+ old.top = w->struts->top;
+ old.bottom = w->struts->bottom;
+ }
+ else
+ {
+ hasOld = FALSE;
+ }
+
+ hasNew = FALSE;
+
+ new.left.x = 0;
+ new.left.y = 0;
+ new.left.width = 0;
+ new.left.height = w->screen->height;
+
+ new.right.x = w->screen->width;
+ new.right.y = 0;
+ new.right.width = 0;
+ new.right.height = w->screen->height;
+
+ new.top.x = 0;
+ new.top.y = 0;
+ new.top.width = w->screen->width;
+ new.top.height = 0;
+
+ new.bottom.x = 0;
+ new.bottom.y = w->screen->height;
+ new.bottom.width = w->screen->width;
+ new.bottom.height = 0;
+
+ result = XGetWindowProperty (w->screen->display->display, w->id,
+ w->screen->display->wmStrutPartialAtom,
+ 0L, 12L, FALSE, XA_CARDINAL, &actual, &format,
+ &n, &left, (unsigned char **) &struts);
+
+ if (result == Success && n && struts)
+ {
+ if (n == 12)
+ {
+ int gap;
+
+ hasNew = TRUE;
+
+ gap = w->screen->width - struts[0] - struts[1];
+ gap -= MIN_EMPTY;
+
+ new.left.width = (int) struts[0] + MIN (0, gap / 2);
+ new.right.width = (int) struts[1] + MIN (0, gap / 2);
+
+ gap = w->screen->height - struts[2] - struts[3];
+ gap -= MIN_EMPTY;
+
+ new.top.height = (int) struts[2] + MIN (0, gap / 2);
+ new.bottom.height = (int) struts[3] + MIN (0, gap / 2);
+
+ new.right.x = w->screen->width - new.right.width;
+ new.bottom.y = w->screen->height - new.bottom.height;
+
+ new.left.y = struts[4];
+ new.left.height = struts[5] - new.left.y + 1;
+ new.right.y = struts[6];
+ new.right.height = struts[7] - new.right.y + 1;
+
+ new.top.x = struts[8];
+ new.top.width = struts[9] - new.top.x + 1;
+ new.bottom.x = struts[10];
+ new.bottom.width = struts[11] - new.bottom.x + 1;
+ }
+
+ XFree (struts);
+ }
+
+ if (!hasNew)
+ {
+ result = XGetWindowProperty (w->screen->display->display, w->id,
+ w->screen->display->wmStrutAtom,
+ 0L, 4L, FALSE, XA_CARDINAL,
+ &actual, &format, &n, &left,
+ (unsigned char **) &struts);
+
+ if (result == Success && n && struts)
+ {
+ if (n == 4)
+ {
+ int gap;
+
+ hasNew = TRUE;
+
+ gap = w->screen->width - struts[0] - struts[1];
+ gap -= MIN_EMPTY;
+
+ new.left.width = (int) struts[0] + MIN (0, gap / 2);
+ new.right.width = (int) struts[1] + MIN (0, gap / 2);
+
+ gap = w->screen->height - struts[2] - struts[3];
+ gap -= MIN_EMPTY;
+
+ new.top.height = (int) struts[2] + MIN (0, gap / 2);
+ new.bottom.height = (int) struts[3] + MIN (0, gap / 2);
+
+ new.left.x = 0;
+ new.right.x = w->screen->width - new.right.width;
+
+ new.top.y = 0;
+ new.bottom.y = w->screen->height - new.bottom.height;
+ }
+
+ XFree (struts);
+ }
+ }
+
+ if (hasOld != hasNew || (hasNew && hasOld &&
+ memcmp (&new, &old, sizeof (CompStruts))))
+ {
+ if (hasNew)
+ {
+ if (!w->struts)
+ w->struts = malloc (sizeof (CompStruts));
+
+ *w->struts = new;
+ }
+ else
+ {
+ free (w->struts);
+ w->struts = NULL;
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+void
+addWindow (CompScreen *screen,
+ Window id,
+ Window aboveId)
+{
+ CompWindow *w;
+
+ w = (CompWindow *) malloc (sizeof (CompWindow));
+ if (!w)
+ return;
+
+ w->next = NULL;
+ w->prev = NULL;
+
+ w->mapNum = 0;
+ w->activeNum = 0;
+
+ w->frame = None;
+
+ w->placed = FALSE;
+ w->minimized = FALSE;
+
+ w->startupId = NULL;
+ w->resName = NULL;
+ w->resClass = NULL;
+
+ initTexture (screen, &w->texture);
+
+ w->screen = screen;
+ w->pixmap = None;
+ w->destroyed = FALSE;
+ w->damaged = FALSE;
+
+ w->destroyRefCnt = 1;
+ w->unmapRefCnt = 1;
+
+ w->group = NULL;
+
+ w->damageRects = 0;
+ w->sizeDamage = 0;
+ w->nDamage = 0;
+
+ w->vertices = 0;
+ w->vertexSize = 0;
+ w->indices = 0;
+ w->indexSize = 0;
+ w->vCount = 0;
+
+ w->struts = 0;
+
+ w->input.left = 0;
+ w->input.right = 0;
+ w->input.top = 0;
+ w->input.bottom = 0;
+
+ w->output.left = 0;
+ w->output.right = 0;
+ w->output.top = 0;
+ w->output.bottom = 0;
+
+ w->paint.opacity = OPAQUE;
+ w->paint.brightness = 0xffff;
+ w->paint.saturation = COLOR;
+ w->paint.xScale = 1.0f;
+ w->paint.yScale = 1.0f;
+
+ w->alive = TRUE;
+ w->saturation = COLOR;
+
+ w->scaled = FALSE;
+
+ w->mwmDecor = MwmDecorAll;
+
+ if (screen->windowPrivateLen)
+ {
+ w->privates = malloc (screen->windowPrivateLen * sizeof (CompPrivate));
+ if (!w->privates)
+ {
+ free (w);
+ return;
+ }
+ }
+ else
+ w->privates = 0;
+
+ w->region = XCreateRegion ();
+ if (!w->region)
+ {
+ freeWindow (w);
+ return;
+ }
+
+ w->clip = XCreateRegion ();
+ if (!w->clip)
+ {
+ freeWindow (w);
+ return;
+ }
+
+ if (!XGetWindowAttributes (screen->display->display, id, &w->attrib))
+ {
+ freeWindow (w);
+ return;
+ }
+
+ w->width = w->attrib.width + w->attrib.border_width * 2;
+ w->height = w->attrib.height + w->attrib.border_width * 2;
+
+ w->sizeHints.flags = 0;
+
+ recalcNormalHints (w);
+
+ w->transientFor = None;
+ w->clientLeader = None;
+
+ w->serverX = w->attrib.x;
+ w->serverY = w->attrib.y;
+
+ w->syncAlarm = None;
+ w->syncCounter = 0;
+ w->syncWaitHandle = 0;
+
+ w->syncWait = FALSE;
+ w->syncX = w->attrib.x;
+ w->syncY = w->attrib.y;
+ w->syncWidth = w->attrib.width;
+ w->syncHeight = w->attrib.height;
+ w->syncBorderWidth = w->attrib.border_width;
+
+ w->saveMask = 0;
+
+ XSelectInput (screen->display->display, id,
+ PropertyChangeMask |
+ EnterWindowMask |
+ FocusChangeMask);
+
+ w->id = id;
+
+ XGrabButton (screen->display->display, AnyButton,
+ AnyModifier, w->id, TRUE, ButtonPressMask |
+ ButtonReleaseMask | ButtonMotionMask,
+ GrabModeSync, GrabModeSync, None, None);
+
+ w->inputHint = TRUE;
+ w->alpha = (w->attrib.depth == 32);
+ w->wmType = 0;
+ w->state = 0;
+ w->actions = 0;
+ w->protocols = 0;
+ w->type = CompWindowTypeUnknownMask;
+ w->lastPong = screen->display->lastPing;
+
+ if (screen->display->shapeExtension)
+ XShapeSelectInput (screen->display->display, id, ShapeNotifyMask);
+
+ insertWindowIntoScreen (screen, w, aboveId);
+
+ EMPTY_REGION (w->region);
+
+ if (w->attrib.class != InputOnly)
+ {
+ REGION rect;
+
+ rect.rects = &rect.extents;
+ rect.numRects = rect.size = 1;
+
+ rect.extents.x1 = w->attrib.x;
+ rect.extents.y1 = w->attrib.y;
+ rect.extents.x2 = w->attrib.x + w->width;
+ rect.extents.y2 = w->attrib.y + w->height;
+
+ XUnionRegion (&rect, w->region, w->region);
+
+ initTexture (screen, &w->texture);
+
+ w->damage = XDamageCreate (screen->display->display, id,
+ XDamageReportRawRectangles);
+
+ /* need to check for DisplayModal state on all windows */
+ w->state = getWindowState (w->screen->display, w->id);
+
+ updateWindowClassHints (w);
+ }
+ else
+ {
+ w->damage = None;
+ w->attrib.map_state = IsUnmapped;
+ }
+
+ if (testMode)
+ {
+ static int useAlpha = 0;
+
+ w->attrib.map_state = IsViewable;
+ w->damaged = TRUE;
+
+ w->attrib.width = 0;
+ w->attrib.height = 0;
+
+ bindWindow (w);
+
+ w->alpha = useAlpha;
+ useAlpha = !useAlpha;
+ }
+
+ w->invisible = TRUE;
+
+ if (!w->attrib.override_redirect)
+ {
+ updateNormalHints (w);
+ updateWindowStruts (w);
+
+ updateWmHints (w);
+
+ XGetTransientForHint (w->screen->display->display,
+ w->id, &w->transientFor);
+
+ w->clientLeader = getClientLeader (w);
+
+ w->wmType = getWindowType (w->screen->display, w->id);
+
+ recalcWindowType (w);
+
+ w->mwmDecor = getMwmDecor (w->screen->display, w->id);
+ w->protocols = getProtocols (w->screen->display, w->id);
+
+ if (!(w->type & CompWindowTypeDesktopMask))
+ w->paint.opacity =
+ getWindowProp32 (w->screen->display, w->id,
+ w->screen->display->winOpacityAtom,
+ OPAQUE);
+
+ w->paint.brightness =
+ getWindowProp32 (w->screen->display, w->id,
+ w->screen->display->winBrightnessAtom,
+ BRIGHT);
+
+ if (w->screen->canDoSaturated)
+ {
+ w->saturation =
+ getWindowProp32 (w->screen->display, w->id,
+ w->screen->display->winSaturationAtom,
+ COLOR);
+ if (w->alive)
+ w->paint.saturation = w->saturation;
+ }
+ }
+
+ if (w->attrib.map_state == IsViewable)
+ {
+ w->attrib.map_state = IsUnmapped;
+
+ mapWindow (w);
+
+ if (!w->attrib.override_redirect)
+ updateWindowAttributes (w);
+ }
+
+ windowInitPlugins (w);
+}
+
+void
+removeWindow (CompWindow *w)
+{
+ unhookWindowFromScreen (w->screen, w);
+
+ if (w->attrib.map_state == IsViewable && w->damaged)
+ {
+ if (w->type == CompWindowTypeDesktopMask)
+ w->screen->desktopWindowCount--;
+
+ if (w->struts)
+ updateWorkareaForScreen (w->screen);
+
+ updateClientListForScreen (w->screen);
+ }
+ else if (w->state & CompWindowStateHiddenMask)
+ {
+ updateClientListForScreen (w->screen);
+ }
+
+ windowFiniPlugins (w);
+
+ freeWindow (w);
+}
+
+void
+destroyWindow (CompWindow *w)
+{
+ w->id = 1;
+ w->mapNum = 0;
+
+ w->destroyRefCnt--;
+ if (w->destroyRefCnt)
+ return;
+
+ if (!w->destroyed)
+ {
+ w->destroyed = TRUE;
+ w->screen->pendingDestroys++;
+ }
+}
+
+static void
+sendConfigureNotify (CompWindow *w)
+{
+ XConfigureEvent xev;
+
+ xev.type = ConfigureNotify;
+ xev.event = w->id;
+ xev.window = w->id;
+ xev.x = w->serverX;
+ xev.y = w->serverY;
+ xev.width = w->attrib.width;
+ xev.height = w->attrib.height;
+ xev.border_width = w->attrib.border_width;
+
+ xev.above = (w->prev) ? w->prev->id : None;
+ xev.override_redirect = w->attrib.override_redirect;
+
+ XSendEvent (w->screen->display->display, w->id, FALSE,
+ StructureNotifyMask, (XEvent *) &xev);
+}
+
+void
+mapWindow (CompWindow *w)
+{
+ if (w->attrib.class == InputOnly)
+ return;
+
+ if (w->attrib.map_state == IsViewable)
+ return;
+
+ w->unmapRefCnt = 1;
+
+ w->attrib.map_state = IsViewable;
+
+ setWmState (w->screen->display, NormalState, w->id);
+
+ w->invisible = TRUE;
+ w->damaged = FALSE;
+ w->alive = TRUE;
+
+ w->lastPong = w->screen->display->lastPing;
+
+ w->mapNum = w->screen->mapNum++;
+
+ updateWindowRegion (w);
+
+ if (w->frame)
+ XMapWindow (w->screen->display->display, w->frame);
+
+ if (w->struts)
+ updateWorkareaForScreen (w->screen);
+
+ updateClientListForScreen (w->screen);
+
+ if (w->type & CompWindowTypeDesktopMask)
+ w->screen->desktopWindowCount++;
+
+ if (w->protocols & CompWindowProtocolSyncRequestMask)
+ {
+ sendSyncRequest (w);
+ sendConfigureNotify (w);
+ }
+}
+
+void
+unmapWindow (CompWindow *w)
+{
+ w->mapNum = 0;
+
+ if (w->frame)
+ XUnmapWindow (w->screen->display->display, w->frame);
+
+ w->unmapRefCnt--;
+ if (w->unmapRefCnt > 0)
+ return;
+
+ if (w->attrib.map_state != IsViewable)
+ return;
+
+ if (w->type == CompWindowTypeDesktopMask)
+ w->screen->desktopWindowCount--;
+
+ addWindowDamage (w);
+
+ w->attrib.map_state = IsUnmapped;
+
+ setWmState (w->screen->display, IconicState, w->id);
+
+ w->invisible = TRUE;
+
+ releaseWindow (w);
+
+ if (w->struts)
+ updateWorkareaForScreen (w->screen);
+
+ updateClientListForScreen (w->screen);
+}
+
+static int
+restackWindow (CompWindow *w,
+ Window aboveId)
+{
+ if (w->prev)
+ {
+ if (aboveId && aboveId == w->prev->id)
+ return 0;
+ }
+ else if (aboveId == None)
+ return 0;
+
+ unhookWindowFromScreen (w->screen, w);
+ insertWindowIntoScreen (w->screen, w, aboveId);
+
+ updateClientListForScreen (w->screen);
+
+ return 1;
+}
+
+Bool
+resizeWindow (CompWindow *w,
+ int x,
+ int y,
+ int width,
+ int height,
+ int borderWidth)
+{
+ Window frame = None;
+
+ if (x != w->serverX)
+ {
+ frame = w->frame;
+ w->serverX = x;
+ }
+ else
+ x = w->attrib.x;
+
+ if (y != w->serverY)
+ {
+ frame = w->frame;
+ w->serverY = y;
+ }
+ else
+ y = w->attrib.y;
+
+ if (frame)
+ XMoveWindow (w->screen->display->display, frame,
+ w->serverX - w->input.left,
+ w->serverY - w->input.top);
+
+ if (w->attrib.width != width ||
+ w->attrib.height != height ||
+ w->attrib.border_width != borderWidth)
+ {
+ unsigned int pw, ph, actualWidth, actualHeight, ui;
+ Pixmap pixmap = None;
+ Window root;
+ Status result;
+ int i;
+
+ pw = width + borderWidth * 2;
+ ph = height + borderWidth * 2;
+
+ if (!w->invisible)
+ {
+ pixmap = XCompositeNameWindowPixmap (w->screen->display->display,
+ w->id);
+ if (!pixmap)
+ {
+ fprintf (stderr, "%s: XCompositeNameWindowPixmap failed\n",
+ programName);
+
+ return FALSE;
+ }
+
+ result = XGetGeometry (w->screen->display->display, pixmap, &root,
+ &i, &i, &actualWidth, &actualHeight,
+ &ui, &ui);
+
+ if (actualWidth != pw || actualHeight != ph)
+ {
+ XFreePixmap (w->screen->display->display, pixmap);
+
+ return FALSE;
+ }
+ }
+
+ addWindowDamage (w);
+
+ w->attrib.x = x;
+ w->attrib.y = y;
+ w->attrib.width = width;
+ w->attrib.height = height;
+ w->attrib.border_width = borderWidth;
+
+ w->width = pw;
+ w->height = ph;
+
+ releaseWindow (w);
+
+ w->pixmap = pixmap;
+
+ if (w->mapNum)
+ updateWindowRegion (w);
+
+ (*w->screen->windowResizeNotify) (w);
+
+ addWindowDamage (w);
+
+ w->invisible = WINDOW_INVISIBLE (w);
+
+ updateFrameWindow (w);
+ }
+ else if (w->attrib.x != x || w->attrib.y != y)
+ {
+ int dx, dy;
+
+ dx = x - w->attrib.x;
+ dy = y - w->attrib.y;
+
+ moveWindow (w, dx, dy, TRUE);
+ }
+
+ return TRUE;
+}
+
+static void
+syncValueIncrement (XSyncValue *value)
+{
+ XSyncValue one;
+ int overflow;
+
+ XSyncIntToValue (&one, 1);
+ XSyncValueAdd (value, *value, one, &overflow);
+}
+
+static Bool
+initializeSyncCounter (CompWindow *w)
+{
+ XSyncAlarmAttributes values;
+ Atom actual;
+ int result, format;
+ unsigned long n, left;
+ unsigned long *counter;
+
+ if (w->syncCounter)
+ return w->syncAlarm != None;
+
+ if (!(w->protocols & CompWindowProtocolSyncRequestMask))
+ return FALSE;
+
+ result = XGetWindowProperty (w->screen->display->display, w->id,
+ w->screen->display->wmSyncRequestCounterAtom,
+ 0L, 1L, FALSE, XA_CARDINAL, &actual, &format,
+ &n, &left, (unsigned char **) &counter);
+
+ if (result == Success && n && counter)
+ {
+ w->syncCounter = *counter;
+
+ XFree (counter);
+
+ XSyncIntsToValue (&w->syncValue, (unsigned int) rand (), 0);
+ XSyncSetCounter (w->screen->display->display,
+ w->syncCounter,
+ w->syncValue);
+
+ syncValueIncrement (&w->syncValue);
+
+ values.events = TRUE;
+
+ values.trigger.counter = w->syncCounter;
+ values.trigger.wait_value = w->syncValue;
+
+ values.trigger.value_type = XSyncAbsolute;
+ values.trigger.test_type = XSyncPositiveComparison;
+
+ XSyncIntToValue (&values.delta, 1);
+
+ values.events = TRUE;
+
+ compCheckForError (w->screen->display->display);
+
+ /* Note that by default, the alarm increments the trigger value
+ * when it fires until the condition (counter.value < trigger.value)
+ * is FALSE again.
+ */
+ w->syncAlarm = XSyncCreateAlarm (w->screen->display->display,
+ XSyncCACounter |
+ XSyncCAValue |
+ XSyncCAValueType |
+ XSyncCATestType |
+ XSyncCADelta |
+ XSyncCAEvents,
+ &values);
+
+ if (!compCheckForError (w->screen->display->display))
+ return TRUE;
+
+ XSyncDestroyAlarm (w->screen->display->display, w->syncAlarm);
+ w->syncAlarm = None;
+ }
+
+ return FALSE;
+}
+
+static Bool
+syncWaitTimeout (void *closure)
+{
+ CompWindow *w = closure;
+
+ w->syncWaitHandle = 0;
+ handleSyncAlarm (w);
+
+ return FALSE;
+}
+
+void
+sendSyncRequest (CompWindow *w)
+{
+ XClientMessageEvent xev;
+
+ if (w->syncWait)
+ return;
+
+ if (!initializeSyncCounter (w))
+ return;
+
+ xev.type = ClientMessage;
+ xev.window = w->id;
+ xev.message_type = w->screen->display->wmProtocolsAtom;
+ xev.format = 32;
+ xev.data.l[0] = w->screen->display->wmSyncRequestAtom;
+ xev.data.l[1] = CurrentTime;
+ xev.data.l[2] = XSyncValueLow32 (w->syncValue);
+ xev.data.l[3] = XSyncValueHigh32 (w->syncValue);
+ xev.data.l[4] = 0;
+
+ syncValueIncrement (&w->syncValue);
+
+ XSendEvent (w->screen->display->display, w->id, FALSE, 0, (XEvent *) &xev);
+
+ w->syncWait = TRUE;
+ w->syncX = w->serverX;
+ w->syncY = w->serverY;
+ w->syncWidth = w->attrib.width;
+ w->syncHeight = w->attrib.height;
+ w->syncBorderWidth = w->attrib.border_width;
+
+ if (!w->syncWaitHandle)
+ w->syncWaitHandle = compAddTimeout (1000, syncWaitTimeout, w);
+}
+
+void
+configureWindow (CompWindow *w,
+ XConfigureEvent *ce)
+{
+ if (w->syncWait)
+ {
+ w->syncX = ce->x;
+ w->syncY = ce->y;
+ w->syncWidth = ce->width;
+ w->syncHeight = ce->height;
+ w->syncBorderWidth = ce->border_width;
+ }
+ else
+ {
+ resizeWindow (w, ce->x, ce->y, ce->width, ce->height,
+ ce->border_width);
+ }
+
+ w->attrib.override_redirect = ce->override_redirect;
+
+ if (restackWindow (w, ce->above))
+ addWindowDamage (w);
+}
+
+void
+circulateWindow (CompWindow *w,
+ XCirculateEvent *ce)
+{
+ Window newAboveId;
+
+ if (ce->place == PlaceOnTop && w->screen->reverseWindows)
+ newAboveId = w->screen->reverseWindows->id;
+ else
+ newAboveId = 0;
+
+ if (restackWindow (w, newAboveId))
+ addWindowDamage (w);
+}
+
+void
+moveWindow (CompWindow *w,
+ int dx,
+ int dy,
+ Bool damage)
+{
+ if (dx || dy)
+ {
+ if (damage)
+ addWindowDamage (w);
+
+ w->attrib.x += dx;
+ w->attrib.y += dy;
+
+ XOffsetRegion (w->region, dx, dy);
+
+ setWindowMatrix (w);
+
+ w->invisible = WINDOW_INVISIBLE (w);
+
+ (*w->screen->windowMoveNotify) (w, dx, dy);
+
+ if (damage)
+ addWindowDamage (w);
+ }
+}
+
+void
+syncWindowPosition (CompWindow *w)
+{
+ if (w->attrib.x != w->serverX || w->attrib.y != w->serverY)
+ {
+ XMoveWindow (w->screen->display->display, w->id,
+ w->attrib.x,
+ w->attrib.y);
+
+ if (0 && !w->attrib.override_redirect)
+ {
+ XConfigureEvent ce;
+
+ ce.type = ConfigureNotify;
+ ce.display = w->screen->display->display;
+ ce.event = w->id;
+ ce.window = w->id;
+ ce.x = w->attrib.x;
+ ce.y = w->attrib.y;
+ ce.width = w->attrib.width;
+ ce.height = w->attrib.height;
+ ce.border_width = w->attrib.border_width;
+ ce.above = (w->prev) ? w->prev->id : None;
+ ce.override_redirect = FALSE;
+
+ XSendEvent (w->screen->display->display,
+ w->id, FALSE, StructureNotifyMask,
+ (XEvent *) &ce);
+ }
+ }
+}
+
+void
+setWindowScale (CompWindow *w,
+ float xScale,
+ float yScale)
+{
+ if (xScale > 0.999f && xScale < 1.001f &&
+ yScale > 0.999f && yScale < 1.001f)
+ {
+ w->paint.xScale = 1.0f;
+ w->paint.yScale = 1.0f;
+
+ w->scaled = FALSE;
+ }
+ else
+ {
+ w->paint.xScale = xScale;
+ w->paint.yScale = yScale;
+
+ w->scaled = TRUE;
+ }
+}
+
+Bool
+focusWindow (CompWindow *w)
+{
+ if (w->attrib.override_redirect)
+ return FALSE;
+
+ if (!w->mapNum || w->attrib.map_state != IsViewable)
+ return FALSE;
+
+ if (w->attrib.x + w->width <= 0 ||
+ w->attrib.y + w->height <= 0 ||
+ w->attrib.x >= w->screen->width ||
+ w->attrib.y >= w->screen->height)
+ return FALSE;
+
+ if (w->inputHint || (w->protocols & CompWindowProtocolTakeFocusMask))
+ return TRUE;
+
+ return FALSE;
+}
+
+void
+windowResizeNotify (CompWindow *w)
+{
+}
+
+void
+windowMoveNotify (CompWindow *w,
+ int dx,
+ int dy)
+{
+}
+
+void
+windowGrabNotify (CompWindow *w,
+ int x,
+ int y,
+ unsigned int state,
+ unsigned int mask)
+{
+}
+
+void
+windowUngrabNotify (CompWindow *w)
+{
+}
+
+static Bool
+isGroupTransient (CompWindow *w,
+ Window clientLeader)
+{
+ if (!clientLeader)
+ return FALSE;
+
+ if (w->transientFor == None || w->transientFor == w->screen->root)
+ {
+ if (w->type & (CompWindowTypeDialogMask |
+ CompWindowTypeModalDialogMask))
+ {
+ if (w->clientLeader == clientLeader)
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static CompWindow *
+getModalTransient (CompWindow *window)
+{
+ CompWindow *w, *modalTransient;
+
+ modalTransient = window;
+
+ for (w = window->screen->reverseWindows; w; w = w->prev)
+ {
+ if (w == modalTransient || w->mapNum == 0)
+ continue;
+
+ if (w->transientFor == modalTransient->id)
+ {
+ if (w->state & CompWindowStateModalMask)
+ {
+ modalTransient = w;
+ w = window->screen->reverseWindows;
+ }
+ }
+ }
+
+ if (modalTransient == window)
+ {
+ for (w = window->screen->reverseWindows; w; w = w->prev)
+ {
+ if (w == modalTransient || w->mapNum == 0)
+ continue;
+
+ if (isGroupTransient (w, modalTransient->clientLeader))
+ {
+ if (w->state & CompWindowStateModalMask)
+ {
+ modalTransient = w;
+ break;
+ }
+ }
+ }
+ }
+
+ if (modalTransient == window)
+ modalTransient = NULL;
+
+ return modalTransient;
+}
+
+void
+moveInputFocusToWindow (CompWindow *w)
+{
+ CompDisplay *d = w->screen->display;
+ CompWindow *modalTransient;
+
+ modalTransient = getModalTransient (w);
+ if (modalTransient)
+ w = modalTransient;
+
+ if (w->id != d->activeWindow)
+ {
+ if (w->inputHint || (w->protocols & CompWindowProtocolTakeFocusMask))
+ XSetInputFocus (d->display, w->id, RevertToPointerRoot,
+ CurrentTime);
+ }
+}
+
+static Bool
+isAncestorTo (CompWindow *transient,
+ CompWindow *ancestor)
+{
+ if (transient->transientFor)
+ {
+ if (transient->transientFor == ancestor->id)
+ return TRUE;
+
+ transient = findWindowAtScreen (transient->screen,
+ transient->transientFor);
+ if (transient)
+ return isAncestorTo (transient, ancestor);
+ }
+
+ return FALSE;
+}
+
+
+static Bool
+stackLayerCheck (CompWindow *w,
+ Window clientLeader,
+ CompWindow *below)
+{
+ if (w->transientFor == below->id)
+ return TRUE;
+
+ if (isAncestorTo (below, w))
+ return FALSE;
+
+ if (clientLeader && below->clientLeader == clientLeader)
+ if (isGroupTransient (below, clientLeader))
+ return FALSE;
+
+ if (w->state & CompWindowStateAboveMask)
+ {
+ return TRUE;
+ }
+ else if (w->state & CompWindowStateBelowMask)
+ {
+ if (below->state & CompWindowStateBelowMask)
+ return TRUE;
+ }
+ else if (!(below->state & CompWindowStateAboveMask))
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/* goes through the stack, top-down until we find a window we should
+ stack above, normal windows can be stacked above fullscreen windows. */
+static CompWindow *
+findSibilingBelow (CompWindow *w)
+{
+ CompWindow *below;
+ Window clientLeader = w->clientLeader;
+ unsigned int type = w->type;
+
+ /* normal stacking fullscreen windows with below state */
+ if ((type & CompWindowTypeFullscreenMask) &&
+ (w->state & CompWindowStateBelowMask))
+ type = CompWindowTypeNormalMask;
+
+ if (w->transientFor || isGroupTransient (w, clientLeader))
+ clientLeader = None;
+
+ for (below = w->screen->reverseWindows; below; below = below->prev)
+ {
+ if (below == w)
+ continue;
+
+ if (below->attrib.override_redirect)
+ continue;
+
+ if (below->attrib.map_state != IsViewable || below->mapNum == 0)
+ continue;
+
+ /* always above desktop windows */
+ if (below->type & CompWindowTypeDesktopMask)
+ return below;
+
+ switch (type) {
+ case CompWindowTypeDesktopMask:
+ /* desktop window layer */
+ break;
+ case CompWindowTypeFullscreenMask:
+ case CompWindowTypeDockMask:
+ /* fullscreen and dock layer */
+ if (below->type & (CompWindowTypeFullscreenMask |
+ CompWindowTypeDockMask))
+ {
+ if (stackLayerCheck (w, clientLeader, below))
+ return below;
+ }
+ else
+ {
+ return below;
+ }
+ break;
+ default:
+ /* fullscreen and normal layer */
+ if (!(below->type & CompWindowTypeDockMask))
+ {
+ if (stackLayerCheck (w, clientLeader, below))
+ return below;
+ }
+ break;
+ }
+ }
+
+ return NULL;
+}
+
+static void
+saveWindowGeometry (CompWindow *w,
+ int mask)
+{
+ int m = mask & ~w->saveMask;
+
+ /* only save geometry if window has been placed */
+ if (!w->placed)
+ return;
+
+ if (m & CWX)
+ w->saveWc.x = w->attrib.x;
+
+ if (m & CWY)
+ w->saveWc.y = w->attrib.y;
+
+ if (m & CWWidth)
+ w->saveWc.width = w->attrib.width;
+
+ if (m & CWHeight)
+ w->saveWc.height = w->attrib.height;
+
+ if (m & CWBorderWidth)
+ w->saveWc.border_width = w->attrib.border_width;
+
+ w->saveMask |= m;
+}
+
+static int
+restoreWindowGeometry (CompWindow *w,
+ XWindowChanges *xwc,
+ int mask)
+{
+ int m = mask & w->saveMask;
+
+ if (m & CWX)
+ xwc->x = w->saveWc.x;
+
+ if (m & CWY)
+ xwc->y = w->saveWc.y;
+
+ if (m & CWWidth)
+ xwc->width = w->saveWc.width;
+
+ if (m & CWHeight)
+ xwc->height = w->saveWc.height;
+
+ if (m & CWBorderWidth)
+ xwc->border_width = w->saveWc.border_width;
+
+ w->saveMask &= ~mask;
+
+ return m;
+}
+static void
+configureXWindow (Display *dpy,
+ CompWindow *w,
+ unsigned int valueMask,
+ XWindowChanges *xwc)
+{
+ XConfigureWindow (w->screen->display->display, w->id,
+ valueMask, xwc);
+
+ if (w->frame && (valueMask & (CWSibling | CWStackMode)))
+ XConfigureWindow (w->screen->display->display, w->frame,
+ valueMask & (CWSibling | CWStackMode), xwc);
+}
+
+static Bool
+stackTransients (CompWindow *w,
+ CompWindow *avoid,
+ XWindowChanges *xwc)
+{
+ CompWindow *t;
+ Window clientLeader = w->clientLeader;
+
+ if (w->transientFor || isGroupTransient (w, clientLeader))
+ clientLeader = None;
+
+ for (t = w->screen->reverseWindows; t; t = t->prev)
+ {
+ if (t == w || t == avoid)
+ continue;
+
+ if (t->transientFor == w->id || isGroupTransient (t, clientLeader))
+ {
+ if (!stackTransients (t, avoid, xwc))
+ return FALSE;
+
+ if (xwc->sibling == t->id)
+ return FALSE;
+
+ if (t->mapNum)
+ configureXWindow (w->screen->display->display, t,
+ CWSibling | CWStackMode, xwc);
+ }
+ }
+
+ return TRUE;
+}
+
+static void
+stackAncestors (CompWindow *w,
+ XWindowChanges *xwc)
+{
+ if (w->transientFor && xwc->sibling != w->transientFor)
+ {
+ CompWindow *ancestor;
+
+ ancestor = findWindowAtScreen (w->screen, w->transientFor);
+ if (ancestor)
+ {
+ if (!stackTransients (ancestor, w, xwc))
+ return;
+
+ if (ancestor->mapNum)
+ configureXWindow (w->screen->display->display, ancestor,
+ CWSibling | CWStackMode,
+ xwc);
+
+ stackAncestors (ancestor, xwc);
+ }
+ }
+ else if (isGroupTransient (w, w->clientLeader))
+ {
+ CompWindow *a;
+
+ for (a = w->screen->reverseWindows; a; a = a->prev)
+ {
+ if (a->clientLeader == w->clientLeader &&
+ a->transientFor == None &&
+ !isGroupTransient (a, w->clientLeader))
+ {
+ if (xwc->sibling == a->id)
+ break;
+
+ if (!stackTransients (a, w, xwc))
+ break;
+
+ if (a->mapNum)
+ configureXWindow (w->screen->display->display, a,
+ CWSibling | CWStackMode,
+ xwc);
+ }
+ }
+ }
+}
+
+void
+updateWindowAttributes (CompWindow *w)
+{
+ CompWindow *sibiling;
+ XWindowChanges xwc;
+ int mask = 0;
+
+ if (w->state & CompWindowStateHiddenMask)
+ return;
+
+ xwc.stack_mode = Above;
+ xwc.sibling = None;
+
+ sibiling = findSibilingBelow (w);
+ if (sibiling)
+ xwc.sibling = sibiling->id;
+
+ if (xwc.sibling != w->id)
+ {
+ if (w->prev)
+ {
+ if (xwc.sibling == None)
+ {
+ XLowerWindow (w->screen->display->display, w->id);
+ if (w->frame)
+ XLowerWindow (w->screen->display->display, w->frame);
+ }
+ else if (xwc.sibling != w->prev->id)
+ mask |= CWSibling | CWStackMode;
+ }
+ else if (xwc.sibling != None)
+ mask |= CWSibling | CWStackMode;
+ }
+
+ /* only update fullscreen and maximized size if window is visible on
+ current viewport. Size is updated once we switch to the windows
+ viewport. */
+ if (w->attrib.x < w->screen->width && w->attrib.x + w->width > 0)
+ {
+ if (w->type & CompWindowTypeFullscreenMask)
+ {
+ saveWindowGeometry (w,
+ CWX | CWY | CWWidth | CWHeight |
+ CWBorderWidth);
+
+ xwc.width = w->screen->width;
+ xwc.height = w->screen->height;
+ xwc.border_width = 0;
+
+ mask |= CWWidth | CWHeight | CWBorderWidth;
+ }
+ else
+ {
+ mask |= restoreWindowGeometry (w, &xwc, CWBorderWidth);
+
+ if (w->state & CompWindowStateMaximizedVertMask)
+ {
+ saveWindowGeometry (w, CWY | CWHeight);
+
+ xwc.height = w->screen->workArea.height - w->input.top -
+ w->input.bottom - w->attrib.border_width * 2;
+
+ mask |= CWHeight;
+ }
+ else
+ {
+ mask |= restoreWindowGeometry (w, &xwc, CWY | CWHeight);
+ }
+
+ if (w->state & CompWindowStateMaximizedHorzMask)
+ {
+ saveWindowGeometry (w, CWX | CWWidth);
+
+ xwc.width = w->screen->workArea.width - w->input.left -
+ w->input.right - w->attrib.border_width * 2;
+
+ mask |= CWWidth;
+ }
+ else
+ {
+ mask |= restoreWindowGeometry (w, &xwc, CWX | CWWidth);
+ }
+ }
+ }
+
+ if (mask & (CWWidth | CWHeight))
+ {
+ if (w->type & CompWindowTypeFullscreenMask)
+ {
+ xwc.x = 0;
+ xwc.y = 0;
+
+ mask |= CWX | CWY;
+ }
+ else
+ {
+ int width, height, max;
+
+ width = (mask & CWWidth) ? xwc.width : w->attrib.width;
+ height = (mask & CWHeight) ? xwc.height : w->attrib.height;
+
+ xwc.width = w->attrib.width;
+ xwc.height = w->attrib.height;
+
+ if (constrainNewWindowSize (w, width, height, &width, &height))
+ {
+ xwc.width = width;
+ xwc.height = height;
+ }
+ else
+ mask &= ~(CWWidth | CWHeight);
+
+ if (w->state & CompWindowStateMaximizedVertMask)
+ {
+ if (w->attrib.y < w->screen->workArea.y + w->input.top)
+ {
+ xwc.y = w->screen->workArea.y + w->input.top;
+ mask |= CWY;
+ }
+ else
+ {
+ height = xwc.height + w->attrib.border_width * 2;
+
+ max = w->screen->workArea.y + w->screen->workArea.height;
+ if (w->attrib.y + height + w->input.bottom > max)
+ {
+ xwc.y = max - height - w->input.bottom;
+ mask |= CWY;
+ }
+ }
+ }
+
+ if (w->state & CompWindowStateMaximizedHorzMask)
+ {
+ if (w->attrib.x < w->screen->workArea.x + w->input.left)
+ {
+ xwc.x = w->screen->workArea.x + w->input.left;
+ mask |= CWX;
+ }
+ else
+ {
+ width = xwc.width + w->attrib.border_width * 2;
+
+ max = w->screen->workArea.x + w->screen->workArea.width;
+ if (w->attrib.x + width + w->input.right > max)
+ {
+ xwc.x = max - width - w->input.right;
+ mask |= CWX;
+ }
+ }
+ }
+ }
+ }
+
+ if (!mask)
+ return;
+
+ if (mask & (CWSibling | CWStackMode))
+ {
+ /* a normal window can be stacked above fullscreen windows but we
+ don't wont normal windows to be stacked above dock window so if
+ the sibiling we're stacking above is a fullscreen window we also
+ update all dock windows. */
+ if ((sibiling->type & CompWindowTypeFullscreenMask) &&
+ (!(w->type & (CompWindowTypeFullscreenMask |
+ CompWindowTypeDockMask))))
+ {
+ CompWindow *dw;
+
+ for (dw = w->screen->reverseWindows; dw; dw = dw->prev)
+ if (dw == sibiling)
+ break;
+
+ for (; dw; dw = dw->prev)
+ if (dw->type & CompWindowTypeDockMask)
+ configureXWindow (w->screen->display->display, dw,
+ CWSibling | CWStackMode,
+ &xwc);
+ }
+
+ /* transient children above */
+ if (stackTransients (w, NULL, &xwc))
+ {
+ configureXWindow (w->screen->display->display, w, mask, &xwc);
+
+ /* ancestors, sibilings and sibiling transients below */
+ stackAncestors (w, &xwc);
+ }
+ }
+ else
+ {
+ configureXWindow (w->screen->display->display, w, mask, &xwc);
+ }
+}
+
+void
+activateWindow (CompWindow *w)
+{
+ if (w->state & CompWindowStateHiddenMask)
+ {
+ if (w->minimized)
+ unminimizeWindow (w);
+
+ if (w->screen->showingDesktopMask)
+ leaveShowDesktopMode (w->screen);
+
+ showWindow (w);
+ }
+
+ if (w->state & CompWindowStateHiddenMask)
+ return;
+
+ updateWindowAttributes (w);
+
+ if (!(w->type & CompWindowTypeDockMask))
+ moveInputFocusToWindow (w);
+}
+
+void
+closeWindow (CompWindow *w)
+{
+ CompDisplay *display = w->screen->display;
+
+ if (w->actions & CompWindowActionCloseMask)
+ {
+ XEvent ev;
+
+ ev.type = ClientMessage;
+ ev.xclient.window = w->id;
+ ev.xclient.message_type = display->wmProtocolsAtom;
+ ev.xclient.format = 32;
+ ev.xclient.data.l[0] = display->wmDeleteWindowAtom;
+ ev.xclient.data.l[1] = CurrentTime;
+ ev.xclient.data.l[2] = 0;
+ ev.xclient.data.l[3] = 0;
+ ev.xclient.data.l[4] = 0;
+
+ XSendEvent (display->display, w->id, FALSE, NoEventMask, &ev);
+ }
+ else
+ {
+ XKillClient (display->display, w->id);
+ }
+}
+
+void
+getOuterRectOfWindow (CompWindow *w,
+ XRectangle *r)
+{
+ r->x = w->attrib.x - w->input.left;
+ r->y = w->attrib.y - w->input.top;
+ r->width = w->width + w->input.left + w->input.right;
+ r->height = w->height + w->input.top + w->input.bottom;
+}
+
+Bool
+constrainNewWindowSize (CompWindow *w,
+ int width,
+ int height,
+ int *newWidth,
+ int *newHeight)
+{
+ const XSizeHints *hints = &w->sizeHints;
+ int min_width = 0;
+ int min_height = 0;
+ int base_width = 0;
+ int base_height = 0;
+ int xinc = 1;
+ int yinc = 1;
+ int max_width = MAXSHORT;
+ int max_height = MAXSHORT;
+
+ /* Ater gdk_window_constrain_size(), which is partially borrowed from fvwm.
+ *
+ * Copyright 1993, Robert Nation
+ * You may use this code for any purpose, as long as the original
+ * copyright remains in the source code and all documentation
+ *
+ * which in turn borrows parts of the algorithm from uwm
+ */
+
+#define FLOOR(value, base) (((int) ((value) / (base))) * (base))
+#define FLOOR64(value, base) (((uint64_t) ((value) / (base))) * (base))
+#define CLAMP(v, min, max) ((v) <= (min) ? (min) : (v) >= (max) ? (max) : (v))
+
+ if ((hints->flags & PBaseSize) && (hints->flags & PMinSize))
+ {
+ base_width = hints->base_width;
+ base_height = hints->base_height;
+ min_width = hints->min_width;
+ min_height = hints->min_height;
+ }
+ else if (hints->flags & PBaseSize)
+ {
+ base_width = hints->base_width;
+ base_height = hints->base_height;
+ min_width = hints->base_width;
+ min_height = hints->base_height;
+ }
+ else if (hints->flags & PMinSize)
+ {
+ base_width = hints->min_width;
+ base_height = hints->min_height;
+ min_width = hints->min_width;
+ min_height = hints->min_height;
+ }
+
+ if (hints->flags & PMaxSize)
+ {
+ max_width = hints->max_width ;
+ max_height = hints->max_height;
+ }
+
+ if (hints->flags & PResizeInc)
+ {
+ xinc = MAX (xinc, hints->width_inc);
+ yinc = MAX (yinc, hints->height_inc);
+ }
+
+ /* clamp width and height to min and max values */
+ width = CLAMP (width, min_width, max_width);
+ height = CLAMP (height, min_height, max_height);
+
+ /* shrink to base + N * inc */
+ width = base_width + FLOOR (width - base_width, xinc);
+ height = base_height + FLOOR (height - base_height, yinc);
+
+ /* constrain aspect ratio, according to:
+ *
+ * min_aspect.x width max_aspect.x
+ * ------------ <= -------- <= -----------
+ * min_aspect.y height max_aspect.y
+ */
+ if (hints->flags & PAspect &&
+ hints->min_aspect.y > 0 && hints->max_aspect.x > 0)
+ {
+ /* Use 64 bit arithmetic to prevent overflow */
+
+ uint64_t min_aspect_x = hints->min_aspect.x;
+ uint64_t min_aspect_y = hints->min_aspect.y;
+ uint64_t max_aspect_x = hints->max_aspect.x;
+ uint64_t max_aspect_y = hints->max_aspect.y;
+ uint64_t delta;
+
+ if (min_aspect_x * height > width * min_aspect_y)
+ {
+ delta = FLOOR64 (height - width * min_aspect_y / min_aspect_x, yinc);
+ if (height - delta >= min_height)
+ height -= delta;
+ else
+ {
+ delta = FLOOR64 (height * min_aspect_x / min_aspect_y - width,
+ xinc);
+ if (width + delta <= max_width)
+ width += delta;
+ }
+ }
+
+ if (width * max_aspect_y > max_aspect_x * height)
+ {
+ delta = FLOOR64 (width - height * max_aspect_x / max_aspect_y, xinc);
+ if (width - delta >= min_width)
+ width -= delta;
+ else
+ {
+ delta = FLOOR64 (width * min_aspect_y / max_aspect_y - height,
+ yinc);
+ if (height + delta <= max_height)
+ height += delta;
+ }
+ }
+ }
+
+#undef FLOOR
+#undef FLOOR64
+
+ if (width != w->attrib.width || height != w->attrib.height)
+ {
+ *newWidth = width;
+ *newHeight = height;
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+void
+hideWindow (CompWindow *w)
+{
+ if (w->attrib.map_state != IsViewable)
+ return;
+
+ if (w->state & CompWindowStateHiddenMask)
+ return;
+
+ if (!w->minimized && !(w->type & w->screen->showingDesktopMask))
+ return;
+
+ w->state |= CompWindowStateHiddenMask;
+
+ XUnmapWindow (w->screen->display->display, w->id);
+
+ setWindowState (w->screen->display, w->state, w->id);
+}
+
+void
+showWindow (CompWindow *w)
+{
+ if (!(w->state & CompWindowStateHiddenMask))
+ return;
+
+ if (w->minimized || (w->type & w->screen->showingDesktopMask))
+ return;
+
+ w->state &= ~CompWindowStateHiddenMask;
+
+ XMapWindow (w->screen->display->display, w->id);
+
+ setWindowState (w->screen->display, w->state, w->id);
+}
+
+static void
+minimizeTransients (CompWindow *w,
+ void *closure)
+{
+ CompWindow *ancestor = closure;
+
+ if (w->transientFor == ancestor->id ||
+ isGroupTransient (w, ancestor->clientLeader))
+ minimizeWindow (w);
+}
+
+void
+minimizeWindow (CompWindow *w)
+{
+ if (!w->minimized)
+ {
+ w->minimized = TRUE;
+
+ forEachWindowOnScreen (w->screen, minimizeTransients, (void *) w);
+
+ hideWindow (w);
+ }
+}
+
+static void
+unminimizeTransients (CompWindow *w,
+ void *closure)
+{
+ CompWindow *ancestor = closure;
+
+ if (w->transientFor == ancestor->id ||
+ isGroupTransient (w, ancestor->clientLeader))
+ unminimizeWindow (w);
+}
+
+void
+unminimizeWindow (CompWindow *w)
+{
+ if (w->minimized)
+ {
+ w->minimized = FALSE;
+
+ showWindow (w);
+
+ forEachWindowOnScreen (w->screen, unminimizeTransients, (void *) w);
+ }
+}