diff options
author | David Reveman <c99drn@cs.umu.se> | 2006-02-09 06:03:09 +0000 |
---|---|---|
committer | David Reveman <c99drn@cs.umu.se> | 2006-02-09 06:03:09 +0000 |
commit | 9959c2b13ded64a5e66359a8097250dc9d87fc1c (patch) | |
tree | 23478d196cd4acb4a9d2949c0438e9df75c2e8d6 |
Initial revision
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 @@ -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 @@ -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 Binary files differnew file mode 100644 index 00000000..d2b62315 --- /dev/null +++ b/images/background.png diff --git a/images/window.png b/images/window.png Binary files differnew file mode 100644 index 00000000..06742e08 --- /dev/null +++ b/images/window.png 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 = ®.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, ®); + 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 = ®ion.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, ®ion); + } + } + + 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 = ®ion.extents; + region.numRects = region.size = 1; + + region.extents.x1 = ww->model->topLeft.x; + region.extents.y1 = ww->model->topLeft.y; + region.extents.x2 = ww->model->bottomRight.x + 0.5f; + region.extents.y2 = ww->model->bottomRight.y + 0.5f; + + damageScreenRegion (w->screen, ®ion); + + return TRUE; + } + } + + UNWRAP (ws, w->screen, damageWindowRect); + status = (*w->screen->damageWindowRect) (w, initial, rect); + WRAP (ws, w->screen, damageWindowRect, wobblyDamageWindowRect); + + if (initial) + { + if (isWobblyWin (w) && (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, ®ion.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 = ®ion.extents; + region.numRects = region.size = 1; + + damageWindowRegion (w, ®ion); + + 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 = ®.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, ®); + } + 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 = ®.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, ®); + + /* 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, ®); + + /* 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, ®); + + /* 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, ®); + } +} + +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, ®ion.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 = ®ion.extents; + region.numRects = region.size = 1; + + damageWindowRegion (w, ®ion); + } + } +} + +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); + } +} |