diff options
author | Zack Rusin <zack@kde.org> | 2005-12-31 06:21:45 +0000 |
---|---|---|
committer | Zack Rusin <zack@kde.org> | 2005-12-31 06:21:45 +0000 |
commit | e4b17d9630385493c8bdf29b67a831de9374c939 (patch) | |
tree | 33649c485493ee9b6ff22b7b0cafd21d61fd954c |
adding glxcompmgr. currently depends on some mesa patches (will add them in
a second)
-rw-r--r-- | AUTHORS | 1 | ||||
-rw-r--r-- | COPYING | 21 | ||||
-rw-r--r-- | ChangeLog | 0 | ||||
-rw-r--r-- | INSTALL | 9 | ||||
-rw-r--r-- | Makefile.am | 8 | ||||
-rw-r--r-- | NEWS | 0 | ||||
-rw-r--r-- | README | 8 | ||||
-rwxr-xr-x | autogen.sh | 13 | ||||
-rw-r--r-- | configure.ac | 137 | ||||
-rw-r--r-- | glxcomp.pc.in | 12 | ||||
-rw-r--r-- | images/Makefile.am | 8 | ||||
-rw-r--r-- | images/background.png | bin | 0 -> 135524 bytes | |||
-rw-r--r-- | images/window.png | bin | 0 -> 9989 bytes | |||
-rw-r--r-- | include/Makefile.am | 4 | ||||
-rw-r--r-- | include/comp.h | 942 | ||||
-rw-r--r-- | include/region.h | 198 | ||||
-rw-r--r-- | plugins/Makefile.am | 49 | ||||
-rw-r--r-- | plugins/cube.c | 701 | ||||
-rw-r--r-- | plugins/expose.c | 1073 | ||||
-rw-r--r-- | plugins/fade.c | 437 | ||||
-rw-r--r-- | plugins/gconf.c | 1110 | ||||
-rw-r--r-- | plugins/rotate.c | 807 | ||||
-rw-r--r-- | plugins/wobbly.c | 1496 | ||||
-rw-r--r-- | plugins/zoom.c | 690 | ||||
-rw-r--r-- | src/Makefile.am | 22 | ||||
-rw-r--r-- | src/display.c | 1149 | ||||
-rw-r--r-- | src/event.c | 268 | ||||
-rw-r--r-- | src/glxcompmgr.c | 146 | ||||
-rw-r--r-- | src/option.c | 256 | ||||
-rw-r--r-- | src/paint.c | 555 | ||||
-rw-r--r-- | src/plugin.c | 358 | ||||
-rw-r--r-- | src/privates.c | 68 | ||||
-rw-r--r-- | src/readpng.c | 199 | ||||
-rw-r--r-- | src/screen.c | 1294 | ||||
-rw-r--r-- | src/texture.c | 286 | ||||
-rw-r--r-- | src/window.c | 605 |
36 files changed, 12930 insertions, 0 deletions
@@ -0,0 +1 @@ +David Reveman <davidr@novell.com> @@ -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 0000000..e69de29 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,9 @@ +glxcompmgr uses automake, in order to generate the Makefiles for glxcompmgr 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 0000000..b888c76 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,8 @@ +SUBDIRS = include src plugins images + +EXTRA_DIST = \ + COPYING \ + glxcomp.pc.in + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = glxcomp.pc @@ -0,0 +1,8 @@ +glxcompmgr - OpenGL compositing manager + +glxcompmgr is an OpenGL compositing manager that use GLX_MESA_render_texture +for binding redirected top-level windows to texture objects. It has a flexible +plug-in system and it is designed to run well on most graphics hardware. + +David Reveman +davidr@novell.com diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..e81f989 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,13 @@ +#! /bin/sh + +srcdir=`dirname $0` +test -z "$srcdir" && srcdir=. + +ORIGDIR=`pwd` +cd $srcdir + +autoreconf -v --install || exit 1 +cd $ORIGDIR || exit $? + +$srcdir/configure --enable-maintainer-mode "$@" + diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..002ee8c --- /dev/null +++ b/configure.ac @@ -0,0 +1,137 @@ +AC_PREREQ(2.57) + +AC_INIT([glxcompmgr], [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/glxcomp +AC_SUBST(plugindir) + +imagedir=$datadir/glxcomp +AC_SUBST(imagedir) + +GLXCOMP_REQUIRES="libpng xcomposite xfixes xdamage" +PKG_CHECK_MODULES(GLXCOMP, $GLXCOMP_REQUIRES) +AC_SUBST(GLXCOMP_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(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_OUTPUT([ +glxcomp.pc +Makefile +src/Makefile +include/Makefile +plugins/Makefile +images/Makefile +]) + +echo "" +echo "the following optional plugins will be compiled:" +echo " gconf: $use_gconf" +echo "" +echo "and the following optional features will be compiled:" +echo " svg: $use_libsvg_cairo" +echo "" diff --git a/glxcomp.pc.in b/glxcomp.pc.in new file mode 100644 index 0000000..bd6ba1c --- /dev/null +++ b/glxcomp.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/images/Makefile.am b/images/Makefile.am new file mode 100644 index 0000000..f3b7aad --- /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 0000000..c3285c6 --- /dev/null +++ b/images/background.png diff --git a/images/window.png b/images/window.png Binary files differnew file mode 100644 index 0000000..06742e0 --- /dev/null +++ b/images/window.png diff --git a/include/Makefile.am b/include/Makefile.am new file mode 100644 index 0000000..04b985e --- /dev/null +++ b/include/Makefile.am @@ -0,0 +1,4 @@ +glxcompincludedir = $(includedir)/glxcomp +glxcompinclude_HEADERS = \ + comp.h \ + region.h diff --git a/include/comp.h b/include/comp.h new file mode 100644 index 0000000..808ba84 --- /dev/null +++ b/include/comp.h @@ -0,0 +1,942 @@ +/* + * 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 _COMP_H +#define _COMP_H + +#include <sys/time.h> + +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/extensions/Xdamage.h> + +#include <GL/gl.h> +#include <GL/glx.h> + +#include <region.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 OPAQUE 0xffff + +extern char *programName; +extern char **programArgv; +extern int programArgc; +extern char *backgroundImage; +extern char *windowImage; +extern REGION emptyRegion; +extern GLushort defaultColor[4]; +extern Window currentRoot; +extern Bool testMode; +extern Bool restartSignal; + +extern int defaultRefreshRate; +extern char *defaultTextureFilter; + +#define RESTRICT_VALUE(value, min, max) \ + (((value) < (min)) ? (min): ((value) > (max)) ? (max) : (value)) + + +/* 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); + + +/* 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); + + +/* display.c */ + +#define COMP_DISPLAY_OPTION_ACTIVE_PLUGINS 0 +#define COMP_DISPLAY_OPTION_TEXTURE_FILTER 1 +#define COMP_DISPLAY_OPTION_NUM 2 + +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 void (*HandleDamageEventProc) (CompDisplay *display, + XDamageNotifyEvent *event); + +typedef Bool (*CallBackProc) (void *closure); + +struct _CompDisplay { + Display *display; + CompScreen *screens; + + char *screenPrivateIndices; + int screenPrivateLen; + + int compositeEvent, compositeError, compositeOpcode; + int damageEvent, damageError; + + Bool shapeExtension; + int shapeEvent, shapeError; + + Atom winTypeAtom; + Atom winDesktopAtom; + Atom winDockAtom; + Atom winToolbarAtom; + Atom winMenuAtom; + Atom winUtilAtom; + Atom winSplashAtom; + Atom winDialogAtom; + Atom winNormalAtom; + Atom winOpacityAtom; + Atom winActiveAtom; + + Atom wmStateAtom; + Atom wmDeleteWindowAtom; + + Atom xBackgroundAtom[2]; + + GLenum textureFilter; + + unsigned int modMask[CompModNum]; + + CompOption opt[COMP_DISPLAY_OPTION_NUM]; + + CompOptionValue plugin; + Bool dirtyPluginList; + + SetDisplayOptionProc setDisplayOption; + SetDisplayOptionForPluginProc setDisplayOptionForPlugin; + + InitPluginForDisplayProc initPluginForDisplay; + FiniPluginForDisplayProc finiPluginForDisplay; + + HandleEventProc handleEvent; + HandleDamageEventProc handleDamageEvent; + + CompPrivate *privates; +}; + +extern CompDisplay *compDisplays; + +int +allocateDisplayPrivateIndex (void); + +void +freeDisplayPrivateIndex (int index); + +CompOption * +compGetDisplayOptions (CompDisplay *display, + int *count); + + +typedef int CompTimeoutHandle; + +CompTimeoutHandle +compAddTimeout (int time, + CallBackProc callBack, + void *closure); + +void +compRemoveTimeout (CompTimeoutHandle handle); + +int +compCheckForError (void); + +Bool +addDisplay (char *name, + char **plugin, + int nPlugin); + +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); + + +/* 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 +handleDamageEvent (CompDisplay *display, + XDamageNotifyEvent *event); + + +/* paint.c */ + +#define MULTIPLY_USHORT(us1, us2) \ + (((GLuint) (us1) * (GLuint) (us2)) / 0xffff) + +/* 0.5 + 0.5 * tan (FOV) */ +#define BASE_Z_TRANSLATE 1.366025404f + +typedef struct _ScreenPaintAttrib { + GLfloat xRotate; + GLfloat yRotate; + GLfloat vRotate; + GLfloat xTranslate; + GLfloat yTranslate; + GLfloat zTranslate; +} ScreenPaintAttrib; + +typedef struct _WindowPaintAttrib { + GLushort opacity; + GLfloat xTranslate; + GLfloat yTranslate; + GLfloat xScale; + GLfloat yScale; +} WindowPaintAttrib; + +extern ScreenPaintAttrib defaultScreenPaintAttrib; +extern WindowPaintAttrib defaultWindowPaintAttrib; + +#define X_WINDOW_TO_TEXTURE_SPACE(w, _x) ((w)->texture.dx * (_x)) +#define Y_WINDOW_TO_TEXTURE_SPACE(w, _y) \ + ((w)->texture.dy * ((w)->height - (_y))) + +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, + const WindowPaintAttrib *wAttrib, + Region region, + unsigned int mask); + +typedef void (*PaintTransformedScreenProc) (CompScreen *screen, + const ScreenPaintAttrib *sAttrib, + const WindowPaintAttrib *wAttrib, + unsigned int mask); + + +#define PAINT_WINDOW_SOLID_MASK (1 << 0) +#define PAINT_WINDOW_TRANSLUCENT_MASK (1 << 1) +#define PAINT_WINDOW_ON_TRANSFORMED_SCREEN_MASK (1 << 2) + +typedef Bool (*PaintWindowProc) (CompWindow *window, + const WindowPaintAttrib *attrib, + Region region, + unsigned int mask); + +#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 +paintTransformedScreen (CompScreen *screen, + const ScreenPaintAttrib *sAttrib, + const WindowPaintAttrib *wAttrib, + unsigned int mask); + +Bool +paintScreen (CompScreen *screen, + const ScreenPaintAttrib *sAttrib, + const WindowPaintAttrib *wAttrib, + Region region, + 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; +}; + +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 +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 +disableTexture (CompTexture *texture); + + +/* screen.c */ + +#define COMP_SCREEN_OPTION_REFRESH_RATE 0 +#define COMP_SCREEN_OPTION_NUM 1 + +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 Bool (*GLXQueryDrawableProc) (Display *display, + GLXDrawable drawable, + int attribute, + unsigned int *value); + +#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 void (*InvisibleWindowMoveProc) (CompWindow *w, + int dx, + int dy); + + +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; + +struct _CompScreen { + CompScreen *next; + CompDisplay *display; + CompWindow *windows; + CompWindow *reverseWindows; + + char *windowPrivateIndices; + int windowPrivateLen; + + Colormap colormap; + int screenNum; + int width; + int height; + REGION region; + Region damage; + Bool allDamaged; + Window root; + Window fake[2]; + XWindowAttributes attrib; + Window grabWindow; + XVisualInfo *glxPixmapVisuals[MAX_DEPTH + 1]; + int textureRectangle; + int textureNonPowerOfTwo; + Cursor invisibleCursor; + XRectangle *exposeRects; + int sizeExpose; + int nExpose; + CompTexture backgroundTexture; + int backgroundWidth; + int backgroundHeight; + unsigned int pendingDestroys; + int desktopWindowCount; + KeyCode escapeKeyCode; + + 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; + + GLint stencilRef; + + Window activeWindow; + + GLXGetProcAddressProc getProcAddress; + GLXBindTexImageProc bindTexImage; + GLXReleaseTexImageProc releaseTexImage; + GLXQueryDrawableProc queryDrawable; + + 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; + InvisibleWindowMoveProc invisibleWindowMove; + + 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); + +Bool +addScreen (CompDisplay *display, + int screenNum); + +void +damageScreenRegion (CompScreen *screen, + Region region); + +void +damageScreen (CompScreen *screen); + +void +insertWindowIntoScreen (CompScreen *s, + CompWindow *w, + Window aboveId); + +void +unhookWindowFromScreen (CompScreen *s, + CompWindow *w); + +CompWindow * +findWindowAtScreen (CompScreen *s, + Window id); + +CompWindow * +findClientWindowAtScreen (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); + + +/* window.c */ + +#define WINDOW_INVISIBLE(w) \ + ((w)->attrib.map_state != IsViewable || \ + (w)->attrib.x + (w)->width <= 0 || \ + (w)->attrib.y + (w)->height <= 0 || \ + (w)->attrib.x >= (w)->screen->width || \ + (w)->attrib.y >= (w)->screen->height) + +typedef Bool (*InitPluginForWindowProc) (CompPlugin *plugin, + CompWindow *window); +typedef void (*FiniPluginForWindowProc) (CompPlugin *plugin, + CompWindow *window); + +struct _CompWindow { + CompScreen *screen; + CompWindow *next; + CompWindow *prev; + + int refcnt; + Window id; + Window client; + XWindowAttributes attrib; + Pixmap pixmap; + CompTexture texture; + Damage damage; + Bool alpha; + GLint width; + GLint height; + Region region; + Region clip; + Atom type; + Bool invisible; + GLushort opacity; + Bool destroyed; + + CompPrivate *privates; +}; + +int +allocateWindowPrivateIndex (CompScreen *screen); + +void +freeWindowPrivateIndex (CompScreen *screen, + int index); + + +Atom +getWindowType (CompDisplay *display, + Window id); + +Window +getActiveWindow (CompDisplay *display, + Window root); + +unsigned short +getWindowOpacity (CompDisplay *display, + Window id); + +void +updateWindowRegion (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 +configureWindow (CompWindow *w, + XConfigureEvent *ce); + +void +circulateWindow (CompWindow *w, + XCirculateEvent *ce); + +void +addWindowDamage (CompWindow *w); + +void +invisibleWindowMove (CompWindow *w, + int dx, + int dy); + + +/* plugin.c */ + +typedef Bool (*InitPluginProc) (CompPlugin *plugin); +typedef void (*FiniPluginProc) (CompPlugin *plugin); + +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; +} 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/include/region.h b/include/region.h new file mode 100644 index 0000000..5ec983a --- /dev/null +++ b/include/region.h @@ -0,0 +1,198 @@ +/* $Xorg: region.h,v 1.4 2001/02/09 02:03:40 xorgcvs Exp $ */ +/************************************************************************ + +Copyright 1987, 1998 The Open Group + +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. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + + +Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +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 Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL 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. + +************************************************************************/ + +#ifndef _XREGION_H +#define _XREGION_H + +typedef struct { + short x1, x2, y1, y2; +} Box, BOX, BoxRec, *BoxPtr; + +typedef struct { + short x, y, width, height; +}RECTANGLE, RectangleRec, *RectanglePtr; + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef MAXSHORT +#define MAXSHORT 32767 +#endif +#ifndef MINSHORT +#define MINSHORT -MAXSHORT +#endif +#ifndef MAX +#define MAX(a,b) (((a) > (b)) ? (a) : (b)) +#endif +#ifndef MIN +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#endif + + +/* + * clip region + */ + +typedef struct _XRegion { + long size; + long numRects; + BOX *rects; + BOX extents; +} REGION; + +/* Xutil.h contains the declaration: + * typedef struct _XRegion *Region; + */ + +/* 1 if two BOXs overlap. + * 0 if two BOXs do not overlap. + * Remember, x2 and y2 are not in the region + */ +#define EXTENTCHECK(r1, r2) \ + ((r1)->x2 > (r2)->x1 && \ + (r1)->x1 < (r2)->x2 && \ + (r1)->y2 > (r2)->y1 && \ + (r1)->y1 < (r2)->y2) + +/* + * update region extents + */ +#define EXTENTS(r,idRect){\ + if((r)->x1 < (idRect)->extents.x1)\ + (idRect)->extents.x1 = (r)->x1;\ + if((r)->y1 < (idRect)->extents.y1)\ + (idRect)->extents.y1 = (r)->y1;\ + if((r)->x2 > (idRect)->extents.x2)\ + (idRect)->extents.x2 = (r)->x2;\ + if((r)->y2 > (idRect)->extents.y2)\ + (idRect)->extents.y2 = (r)->y2;\ + } + +/* + * Check to see if there is enough memory in the present region. + */ +#define MEMCHECK(reg, rect, firstrect){\ + if ((reg)->numRects >= ((reg)->size - 1)){\ + (firstrect) = (BOX *) Xrealloc \ + ((char *)(firstrect), (unsigned) (2 * (sizeof(BOX)) * ((reg)->size)));\ + if ((firstrect) == 0)\ + return(0);\ + (reg)->size *= 2;\ + (rect) = &(firstrect)[(reg)->numRects];\ + }\ + } + +/* this routine checks to see if the previous rectangle is the same + * or subsumes the new rectangle to add. + */ + +#define CHECK_PREVIOUS(Reg, R, Rx1, Ry1, Rx2, Ry2)\ + (!(((Reg)->numRects > 0)&&\ + ((R-1)->y1 == (Ry1)) &&\ + ((R-1)->y2 == (Ry2)) &&\ + ((R-1)->x1 <= (Rx1)) &&\ + ((R-1)->x2 >= (Rx2)))) + +/* add a rectangle to the given Region */ +#define ADDRECT(reg, r, rx1, ry1, rx2, ry2){\ + if (((rx1) < (rx2)) && ((ry1) < (ry2)) &&\ + CHECK_PREVIOUS((reg), (r), (rx1), (ry1), (rx2), (ry2))){\ + (r)->x1 = (rx1);\ + (r)->y1 = (ry1);\ + (r)->x2 = (rx2);\ + (r)->y2 = (ry2);\ + EXTENTS((r), (reg));\ + (reg)->numRects++;\ + (r)++;\ + }\ + } + + + +/* add a rectangle to the given Region */ +#define ADDRECTNOX(reg, r, rx1, ry1, rx2, ry2){\ + if ((rx1 < rx2) && (ry1 < ry2) &&\ + CHECK_PREVIOUS((reg), (r), (rx1), (ry1), (rx2), (ry2))){\ + (r)->x1 = (rx1);\ + (r)->y1 = (ry1);\ + (r)->x2 = (rx2);\ + (r)->y2 = (ry2);\ + (reg)->numRects++;\ + (r)++;\ + }\ + } + +#define EMPTY_REGION(pReg) pReg->numRects = 0 + +#define REGION_NOT_EMPTY(pReg) pReg->numRects + +#define INBOX(r, x, y) \ + ( ( ((r).x2 > x)) && \ + ( ((r).x1 <= x)) && \ + ( ((r).y2 > y)) && \ + ( ((r).y1 <= y)) ) + +/* + * number of points to buffer before sending them off + * to scanlines() : Must be an even number + */ +#define NUMPTSTOBUFFER 200 + +/* + * used to allocate buffers for points and link + * the buffers together + */ +typedef struct _POINTBLOCK { + XPoint pts[NUMPTSTOBUFFER]; + struct _POINTBLOCK *next; +} POINTBLOCK; + +#endif diff --git a/plugins/Makefile.am b/plugins/Makefile.am new file mode 100644 index 0000000..73cc860 --- /dev/null +++ b/plugins/Makefile.am @@ -0,0 +1,49 @@ +libfade_la_LDFLAGS = -avoid-version +libfade_la_LIBADD = @GLXCOMP_LIBS@ +libfade_la_SOURCES = fade.c + +libcube_la_LDFLAGS = -avoid-version +libcube_la_LIBADD = @GLXCOMP_LIBS@ @LIBSVG_CAIRO_LIBS@ +libcube_la_SOURCES = cube.c + +librotate_la_LDFLAGS = -avoid-version +librotate_la_LIBADD = @GLXCOMP_LIBS@ +librotate_la_SOURCES = rotate.c + +libzoom_la_LDFLAGS = -avoid-version +libzoom_la_LIBADD = @GLXCOMP_LIBS@ +libzoom_la_SOURCES = zoom.c + +libexpose_la_LDFLAGS = -avoid-version +libexpose_la_LIBADD = @GLXCOMP_LIBS@ +libexpose_la_SOURCES = expose.c + +libwobbly_la_LDFLAGS = -avoid-version +libwobbly_la_LIBADD = @GLXCOMP_LIBS@ +libwobbly_la_SOURCES = wobbly.c + +if GCONF_PLUGIN +libgconf_la_LDFLAGS = -avoid-version +libgconf_la_LIBADD = @GLXCOMP_LIBS@ @GCONF_LIBS@ +libgconf_la_SOURCES = gconf.c +libgconf_library = libgconf.la +libgconf_includes = @GCONF_CFLAGS@ +endif + +INCLUDES = \ + @GLXCOMP_CFLAGS@ \ + @LIBSVG_CAIRO_CFLAGS@ \ + @GCONF_CFLAGS@ \ + -I$(top_srcdir)/include + +moduledir = $(plugindir) + +module_LTLIBRARIES = \ + libgconf.la \ + libfade.la \ + libcube.la \ + librotate.la \ + libzoom.la \ + libexpose.la \ + libwobbly.la \ + $(libgconf_library) diff --git a/plugins/cube.c b/plugins/cube.c new file mode 100644 index 0000000..03bb793 --- /dev/null +++ b/plugins/cube.c @@ -0,0 +1,701 @@ +/* + * 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 <comp.h> + +#define CUBE_COLOR_RED_DEFAULT 0xffff +#define CUBE_COLOR_GREEN_DEFAULT 0xffff +#define CUBE_COLOR_BLUE_DEFAULT 0xffff + +#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_SVGS 1 +#define CUBE_SCREEN_OPTION_NEXT 2 +#define CUBE_SCREEN_OPTION_PREV 3 +#define CUBE_SCREEN_OPTION_NUM 4 + +typedef struct _CubeScreen { + PaintTransformedScreenProc paintTransformedScreen; + PaintBackgroundProc paintBackground; + + CompOption opt[CUBE_SCREEN_OPTION_NUM]; + + int xrotations; + Bool paintTopBottom; + GLushort color[3]; + GLfloat tc[8]; + + Pixmap pixmap; + 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->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) +{ + int width, height; + + CUBE_SCREEN (s); + + if (!cs->svgNFile) + { + finiTexture (s, &cs->texture); + initTexture (s, &cs->texture); + cubeFiniSvg (s); + cubeInitSvg (s); + return; + } + + if (!cs->pixmap) + { + cairo_surface_t *surface; + Visual *visual; + int depth; + + 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); + } + + switch (cs->texture.target) { + case GL_TEXTURE_RECTANGLE_ARB: + cs->tc[2] = cs->tc[4] = s->width; + cs->tc[5] = cs->tc[7] = s->height; + break; + case GL_TEXTURE_2D: + default: + cs->tc[2] = cs->tc[4] = 1.0f; + cs->tc[5] = cs->tc[7] = 1.0f; + break; + } + + 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 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_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_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 +cubeTranslateWindows (CompScreen *s, + int tx) +{ + CompWindow *w; + + if (tx == 0) + return; + + for (w = s->windows; w; w = w->next) + { + if (w->attrib.map_state != IsViewable) + continue; + + if (w->type == s->display->winDesktopAtom || + w->type == s->display->winDockAtom) + continue; + + (*s->invisibleWindowMove) (w, tx, 0); + } +} + +static void +cubePaintTransformedScreen (CompScreen *s, + const ScreenPaintAttrib *sAttrib, + const WindowPaintAttrib *wAttrib, + unsigned int mask) +{ + ScreenPaintAttrib sa = defaultScreenPaintAttrib; + int xMove = 0; + + CUBE_SCREEN (s); + + sa.xTranslate = sAttrib->xTranslate; + sa.yTranslate = sAttrib->yTranslate; + sa.zTranslate = sAttrib->zTranslate; + + if (sAttrib->xRotate > 0.0f) + { + cs->xrotations = (int) sAttrib->xRotate / 90; + sa.xRotate = sAttrib->xRotate - (cs->xrotations * 90.0f); + } + else + { + cs->xrotations = (int) sAttrib->xRotate / 90; + sa.xRotate = (sAttrib->xRotate - cs->xrotations * 90.0f) + 90.0f; + cs->xrotations--; + } + + if (sa.vRotate > 100.0f) + sa.vRotate = 100.0f; + else if (sAttrib->vRotate < -100.0f) + sa.vRotate = -100.0f; + else + sa.vRotate = sAttrib->vRotate; + + if (mask & PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS_MASK) + glClear (GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + else + glClear (GL_COLOR_BUFFER_BIT); + + UNWRAP (cs, s, paintTransformedScreen); + + cs->paintTopBottom = TRUE; + + if (sAttrib->xRotate != 0.0f) + { + xMove = cs->xrotations * s->width; + cubeTranslateWindows (s, xMove); + + (*s->paintTransformedScreen) (s, &sa, wAttrib, mask); + + xMove += s->width; + cubeTranslateWindows (s, s->width); + + cs->paintTopBottom = FALSE; + } + + sa.yRotate -= 90.0f; + + (*s->paintTransformedScreen) (s, &sa, wAttrib, mask); + + cubeTranslateWindows (s, -xMove); + + WRAP (cs, s, paintTransformedScreen, cubePaintTransformedScreen); +} + +static void +cubePaintBackground (CompScreen *s, + Region region, + unsigned int mask) +{ + GLint stencilRef = s->stencilRef; + + CUBE_SCREEN (s); + + if (cs->paintTopBottom) + { + int first, count, rot; + GLfloat data[] = { + /* top */ + 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, s->width, 0.0f, 0.0f, + + 0.0f, 0.0f, s->width, 0.0f, -1.0f, + 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, + + /* bottom */ + 0.0f, 0.0f, 0.0f, s->height, -1.0f, + 0.0f, 0.0f, s->width, s->height, -1.0f, + + 0.0f, 0.0f, s->width, s->height, 0.0f, + 0.0f, 0.0f, 0.0f, s->height, 0.0f + }; + + rot = 2 * cs->xrotations; + +#define MOD(a,b) ((a) < 0 ? ((b) - ((-(a) - 1) % (b))) - 1 : (a) % (b)) + + data[0] = cs->tc[MOD (0 - rot, 8)]; + data[1] = cs->tc[MOD (1 - rot, 8)]; + data[5] = cs->tc[MOD (2 - rot, 8)]; + data[6] = cs->tc[MOD (3 - rot, 8)]; + + data[10] = cs->tc[MOD (4 - rot, 8)]; + data[11] = cs->tc[MOD (5 - rot, 8)]; + data[15] = cs->tc[MOD (6 - rot, 8)]; + data[16] = cs->tc[MOD (7 - rot, 8)]; + +#undef MOD + + first = 0; + + glVertexPointer (3, GL_FLOAT, sizeof (GLfloat) * 5, data + 2); + glTexCoordPointer (2, GL_FLOAT, sizeof (GLfloat) * 5, data); + + if (cs->texture.name) + { + if (mask & PAINT_BACKGROUND_ON_TRANSFORMED_SCREEN_MASK) + enableTexture (s, &cs->texture, COMP_TEXTURE_FILTER_GOOD); + else + enableTexture (s, &cs->texture, COMP_TEXTURE_FILTER_FAST); + + glDrawArrays (GL_QUADS, first, 4); + + disableTexture (&cs->texture); + + first += 4; + count = 4; + } + else + count = 8; + + glColor3usv (cs->color); + glDrawArrays (GL_QUADS, first, count); + glColor4usv (defaultColor); + } + else + s->stencilRef++; + + UNWRAP (cs, s, paintBackground); + (*s->paintBackground) (s, region, mask); + WRAP (cs, s, paintBackground, cubePaintBackground); + + s->stencilRef = stencilRef; +} + +#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 +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->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; + + 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); + + return TRUE; +} + +static void +cubeFiniScreen (CompPlugin *p, + CompScreen *s) +{ + CUBE_SCREEN (s); + + UNWRAP (cs, s, paintTransformedScreen); + UNWRAP (cs, s, paintBackground); + + 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); +} + +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 +}; + +CompPluginVTable * +getCompPluginInfo (void) +{ + return &cubeVTable; +} diff --git a/plugins/expose.c b/plugins/expose.c new file mode 100644 index 0000000..62900d4 --- /dev/null +++ b/plugins/expose.c @@ -0,0 +1,1073 @@ +/* + * 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 <comp.h> + +#define EXPOSE_SPACING_DEFAULT 25 +#define EXPOSE_SPACING_MIN 0 +#define EXPOSE_SPACING_MAX 250 + +#define EXPOSE_SLOPPY_FOCUS_DEFAULT FALSE + +#define EXPOSE_INITIATE_KEY_DEFAULT "Tab" +#define EXPOSE_INITIATE_MODIFIERS_DEFAULT (CompPressMask | CompSuperMask) + +#define EXPOSE_TERMINATE_KEY_DEFAULT "Super_L" +#define EXPOSE_TERMINATE_MODIFIERS_DEFAULT CompReleaseMask + +#define EXPOSE_NEXT_WINDOW_KEY_DEFAULT "Tab" +#define EXPOSE_NEXT_WINDOW_MODIFIERS_DEFAULT (CompPressMask | CompSuperMask) + +#define EXPOSE_STATE_NONE 0 +#define EXPOSE_STATE_OUT 1 +#define EXPOSE_STATE_WAIT 2 +#define EXPOSE_STATE_IN 3 + +static int displayPrivateIndex; + +typedef struct _ExposeSlot { + int x1, y1, x2, y2; + int line; +} ExposeSlot; + +typedef struct _ExposeDisplay { + int screenPrivateIndex; + HandleEventProc handleEvent; +} ExposeDisplay; + +#define EXPOSE_SCREEN_OPTION_SPACING 0 +#define EXPOSE_SCREEN_OPTION_SLOPPY_FOCUS 1 +#define EXPOSE_SCREEN_OPTION_INITIATE 2 +#define EXPOSE_SCREEN_OPTION_TERMINATE 3 +#define EXPOSE_SCREEN_OPTION_NEXT_WINDOW 4 +#define EXPOSE_SCREEN_OPTION_NUM 5 + +typedef struct _ExposeScreen { + int windowPrivateIndex; + + PreparePaintScreenProc preparePaintScreen; + DonePaintScreenProc donePaintScreen; + PaintScreenProc paintScreen; + PaintWindowProc paintWindow; + + CompOption opt[EXPOSE_SCREEN_OPTION_NUM]; + + int spacing; + + int grabIndex; + + int state; + int moreAdjust; + + Cursor cursor; + + ExposeSlot *slots; + int slotsSize; + int nSlots; + + int *line; + int lineSize; + int nLine; + + /* only used for sorting */ + CompWindow **windows; + int windowsSize; + int nWindows; + + GLfloat scale; +} ExposeScreen; + +typedef struct _ExposeWindow { + ExposeSlot *slot; + + GLfloat xVelocity, yVelocity, scaleVelocity; + GLfloat scale; + GLfloat tx, ty; + Bool adjust; +} ExposeWindow; + + +#define GET_EXPOSE_DISPLAY(d) \ + ((ExposeDisplay *) (d)->privates[displayPrivateIndex].ptr) + +#define EXPOSE_DISPLAY(d) \ + ExposeDisplay *ed = GET_EXPOSE_DISPLAY (d) + +#define GET_EXPOSE_SCREEN(s, ed) \ + ((ExposeScreen *) (s)->privates[(ed)->screenPrivateIndex].ptr) + +#define EXPOSE_SCREEN(s) \ + ExposeScreen *es = GET_EXPOSE_SCREEN (s, GET_EXPOSE_DISPLAY (s->display)) + +#define GET_EXPOSE_WINDOW(w, es) \ + ((ExposeWindow *) (w)->privates[(es)->windowPrivateIndex].ptr) + +#define EXPOSE_WINDOW(w) \ + ExposeWindow *ew = GET_EXPOSE_WINDOW (w, \ + GET_EXPOSE_SCREEN (w->screen, \ + GET_EXPOSE_DISPLAY (w->screen->display))) + +#define NUM_OPTIONS(s) (sizeof ((s)->opt) / sizeof (CompOption)) + +static CompOption * +exposeGetScreenOptions (CompScreen *screen, + int *count) +{ + EXPOSE_SCREEN (screen); + + *count = NUM_OPTIONS (es); + return es->opt; +} + +static Bool +exposeSetScreenOption (CompScreen *screen, + char *name, + CompOptionValue *value) +{ + CompOption *o; + int index; + + EXPOSE_SCREEN (screen); + + o = compFindOption (es->opt, NUM_OPTIONS (es), name, &index); + if (!o) + return FALSE; + + switch (index) { + case EXPOSE_SCREEN_OPTION_SPACING: + if (compSetIntOption (o, value)) + { + es->spacing = o->value.i; + return TRUE; + } + break; + case EXPOSE_SCREEN_OPTION_SLOPPY_FOCUS: + if (compSetBoolOption (o, value)) + return TRUE; + break; + case EXPOSE_SCREEN_OPTION_INITIATE: + if (addScreenBinding (screen, &value->bind)) + { + removeScreenBinding (screen, &o->value.bind); + + if (compSetBindingOption (o, value)) + return TRUE; + } + break; + case EXPOSE_SCREEN_OPTION_TERMINATE: + case EXPOSE_SCREEN_OPTION_NEXT_WINDOW: + if (compSetBindingOption (o, value)) + return TRUE; + break; + default: + break; + } + + return FALSE; +} + +static void +exposeScreenInitOptions (ExposeScreen *es, + Display *display) +{ + CompOption *o; + + o = &es->opt[EXPOSE_SCREEN_OPTION_SPACING]; + o->name = "spacing"; + o->shortDesc = "Spacing"; + o->longDesc = "Space between windows"; + o->type = CompOptionTypeInt; + o->value.i = EXPOSE_SPACING_DEFAULT; + o->rest.i.min = EXPOSE_SPACING_MIN; + o->rest.i.max = EXPOSE_SPACING_MAX; + + o = &es->opt[EXPOSE_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 = EXPOSE_SLOPPY_FOCUS_DEFAULT; + + o = &es->opt[EXPOSE_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 = EXPOSE_INITIATE_MODIFIERS_DEFAULT; + o->value.bind.u.key.keycode = + XKeysymToKeycode (display, + XStringToKeysym (EXPOSE_INITIATE_KEY_DEFAULT)); + + o = &es->opt[EXPOSE_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 = EXPOSE_TERMINATE_MODIFIERS_DEFAULT; + o->value.bind.u.key.keycode = + XKeysymToKeycode (display, + XStringToKeysym (EXPOSE_TERMINATE_KEY_DEFAULT)); + + o = &es->opt[EXPOSE_SCREEN_OPTION_NEXT_WINDOW]; + o->name = "next_window"; + o->shortDesc = "Next Window"; + o->longDesc = "Focus nextwindow"; + o->type = CompOptionTypeBinding; + o->value.bind.type = CompBindingTypeKey; + o->value.bind.u.key.modifiers = EXPOSE_NEXT_WINDOW_MODIFIERS_DEFAULT; + o->value.bind.u.key.keycode = + XKeysymToKeycode (display, + XStringToKeysym (EXPOSE_NEXT_WINDOW_KEY_DEFAULT)); +} + +static Bool +exposePaintWindow (CompWindow *w, + const WindowPaintAttrib *attrib, + Region region, + unsigned int mask) +{ + CompScreen *s = w->screen; + Bool status; + + EXPOSE_SCREEN (s); + + if (es->grabIndex) + { + WindowPaintAttrib exposeAttrib = *attrib; + + EXPOSE_WINDOW (w); + + exposeAttrib.xTranslate += ew->tx; + exposeAttrib.yTranslate += ew->ty; + exposeAttrib.xScale *= ew->scale; + exposeAttrib.yScale *= ew->scale; + + UNWRAP (es, s, paintWindow); + status = (*s->paintWindow) (w, &exposeAttrib, region, mask); + WRAP (es, s, paintWindow, exposePaintWindow); + } + else + { + UNWRAP (es, s, paintWindow); + status = (*s->paintWindow) (w, attrib, region, mask); + WRAP (es, s, paintWindow, exposePaintWindow); + } + + return status; +} + +static Bool +isExposeWin (CompWindow *w) +{ + if (w->attrib.map_state != IsViewable) + return FALSE; + + if (w->type == w->screen->display->winDesktopAtom || + w->type == w->screen->display->winDockAtom) + return FALSE; + + if (w->attrib.x >= w->screen->width || + w->attrib.y >= w->screen->height || + w->attrib.x + w->width <= 0 || + w->attrib.y + w->height <= 0) + return FALSE; + + return TRUE; +} + +static int +compareWindows (const void *elem1, + const void *elem2) +{ + CompWindow *w1 = *((CompWindow **) elem1); + CompWindow *w2 = *((CompWindow **) elem2); + int s1, s2; + + s1 = (w1->screen->width - (w1->attrib.x + w1->attrib.width)) + + (w1->screen->height - (w1->attrib.y + w1->attrib.height)) - + w1->attrib.x - w1->attrib.y; + + s2 = (w2->screen->width - (w2->attrib.x + w2->attrib.width)) + + (w2->screen->height - (w2->attrib.y + w2->attrib.height)) - + w2->attrib.x - w2->attrib.y; + + return s2 - s1; +} + +/* TODO: Place window thumbnails at smarter positions and use + WM_STRUT HINTS instead of static top/bottom offsets == 24 */ +static Bool +layoutThumbs (CompScreen *s) +{ + CompWindow *w; + int i, j, y2; + int cx, cy; + int lineLength, itemsPerLine; + float scaleW, scaleH; + int totalWidth, totalHeight; + + EXPOSE_SCREEN (s); + + cx = cy = es->nWindows = 0; + + for (w = s->windows; w; w = w->next) + { + EXPOSE_WINDOW (w); + + if (ew->slot) + ew->adjust = TRUE; + + ew->slot = 0; + + if (!isExposeWin (w)) + continue; + + if (es->windowsSize <= es->nWindows) + { + es->windows = realloc (es->windows, + sizeof (CompWindow *) * (es->nWindows + 32)); + if (!es->windows) + return FALSE; + + es->windowsSize = es->nWindows + 32; + } + + es->windows[es->nWindows++] = w; + } + + if (!es->nWindows) + return FALSE; + + qsort (es->windows, es->nWindows, sizeof (CompWindow *), compareWindows); + + itemsPerLine = (sqrt (es->nWindows) * s->width) / s->height; + if (itemsPerLine < 1) + itemsPerLine = 1; + + if (es->lineSize <= es->nWindows / itemsPerLine) + { + es->line = realloc (es->line, sizeof (int) * + (es->nWindows / itemsPerLine + 1)); + if (!es->line) + return FALSE; + + es->lineSize = es->nWindows / itemsPerLine + 1; + } + + totalWidth = totalHeight = 0; + + es->line[0] = 0; + es->nLine = 1; + lineLength = itemsPerLine; + + if (es->slotsSize <= es->nWindows) + { + es->slots = realloc (es->slots, sizeof (ExposeSlot) * + (es->nWindows + 1)); + if (!es->slots) + return FALSE; + + es->slotsSize = es->nWindows + 1; + } + es->nSlots = 0; + + for (i = 0; i < es->nWindows; i++) + { + EXPOSE_WINDOW (es->windows[i]); + + w = es->windows[i]; + + /* find a good place between other elements */ + for (j = 0; j < es->nSlots; j++) + { + y2 = es->slots[j].y2 + es->spacing + w->height; + if (w->width < es->slots[j].x2 - es->slots[j].x1 && + y2 <= es->line[es->slots[j].line]) + break; + } + + /* otherwise append or start a new line */ + if (j == es->nSlots) + { + if (lineLength < itemsPerLine) + { + lineLength++; + + es->slots[es->nSlots].x1 = cx; + es->slots[es->nSlots].y1 = cy; + es->slots[es->nSlots].x2 = cx + w->width; + es->slots[es->nSlots].y2 = cy + w->height; + es->slots[es->nSlots].line = es->nLine - 1; + + es->line[es->nLine - 1] = MAX (es->line[es->nLine - 1], + es->slots[es->nSlots].y2); + } + else + { + lineLength = 1; + + cx = es->spacing; + cy = es->line[es->nLine - 1] + es->spacing; + + es->slots[es->nSlots].x1 = cx; + es->slots[es->nSlots].y1 = cy; + es->slots[es->nSlots].x2 = cx + w->width; + es->slots[es->nSlots].y2 = cy + w->height; + es->slots[es->nSlots].line = es->nLine - 1; + + es->line[es->nLine] = es->slots[es->nSlots].y2; + + es->nLine++; + } + + if (es->slots[es->nSlots].y2 > totalHeight) + totalHeight = es->slots[es->nSlots].y2; + } + else + { + es->slots[es->nSlots].x1 = es->slots[j].x1; + es->slots[es->nSlots].y1 = es->slots[j].y2 + es->spacing; + es->slots[es->nSlots].x2 = es->slots[es->nSlots].x1 + w->width; + es->slots[es->nSlots].y2 = es->slots[es->nSlots].y1 + w->height; + es->slots[es->nSlots].line = es->slots[j].line; + + es->slots[j].line = 0; + } + + cx = es->slots[es->nSlots].x2; + if (cx > totalWidth) + totalWidth = cx; + + cx += es->spacing; + + ew->slot = &es->slots[es->nSlots]; + ew->adjust = TRUE; + + es->nSlots++; + } + + totalWidth += es->spacing; + totalHeight += es->spacing; + + scaleW = (GLfloat) (s->width) / totalWidth; + scaleH = (GLfloat) (s->height - 48) / totalHeight; + + es->scale = MIN (MIN (scaleH, scaleW), 1.0f); + + for (i = 0; i < es->nSlots; i++) + { + es->slots[i].y1 = (float) es->slots[i].y1 * es->scale; + es->slots[i].x1 = (float) es->slots[i].x1 * es->scale; + es->slots[i].y1 += 24; + } + + return TRUE; +} + +static int +adjustExposeVelocity (CompWindow *w) +{ + float dx, dy, ds, adjust, amount; + float x1, y1, scale; + + EXPOSE_SCREEN (w->screen); + EXPOSE_WINDOW (w); + + if (ew->slot) + { + x1 = ew->slot->x1; + y1 = ew->slot->y1; + scale = es->scale; + } + else + { + x1 = w->attrib.x; + y1 = w->attrib.y; + scale = 1.0f; + } + + dx = x1 - (w->attrib.x + ew->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; + + ew->xVelocity = (amount * ew->xVelocity + adjust) / (amount + 1.0f); + + dy = y1 - (w->attrib.y + ew->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; + + ew->yVelocity = (amount * ew->yVelocity + adjust) / (amount + 1.0f); + + ds = scale - ew->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; + + ew->scaleVelocity = (amount * ew->scaleVelocity + adjust) / + (amount + 1.0f); + + if (fabs (dx) < 0.1f && fabs (ew->xVelocity) < 0.2f && + fabs (dy) < 0.1f && fabs (ew->yVelocity) < 0.2f && + fabs (ds) < 0.001f && fabs (ew->scaleVelocity) < 0.002f) + { + ew->xVelocity = ew->yVelocity = ew->scaleVelocity = 0.0f; + ew->tx = x1 - w->attrib.x; + ew->ty = y1 - w->attrib.y; + ew->scale = scale; + + return 0; + } + + return 1; +} + +static Bool +exposePaintScreen (CompScreen *s, + const ScreenPaintAttrib *sAttrib, + const WindowPaintAttrib *wAttrib, + Region region, + unsigned int mask) +{ + Bool status; + + EXPOSE_SCREEN (s); + + if (es->grabIndex) + { + mask &= ~PAINT_SCREEN_REGION_MASK; + mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS_MASK; + } + + UNWRAP (es, s, paintScreen); + status = (*s->paintScreen) (s, sAttrib, wAttrib, region, mask); + WRAP (es, s, paintScreen, exposePaintScreen); + + return status; +} + +static void +exposePreparePaintScreen (CompScreen *s, + int msSinceLastPaint) +{ + EXPOSE_SCREEN (s); + + if (es->grabIndex && es->state != EXPOSE_STATE_WAIT) + { + CompWindow *w; + + es->moreAdjust = 0; + + for (w = s->windows; w; w = w->next) + { + EXPOSE_WINDOW (w); + + if (ew->adjust) + { + ew->adjust = adjustExposeVelocity (w); + + es->moreAdjust |= ew->adjust; + + ew->tx += (ew->xVelocity * msSinceLastPaint) / s->redrawTime; + ew->ty += (ew->yVelocity * msSinceLastPaint) / s->redrawTime; + ew->scale += (ew->scaleVelocity * msSinceLastPaint) / + s->redrawTime; + } + } + } + + UNWRAP (es, s, preparePaintScreen); + (*s->preparePaintScreen) (s, msSinceLastPaint); + WRAP (es, s, preparePaintScreen, exposePreparePaintScreen); +} + +static void +exposeDonePaintScreen (CompScreen *s) +{ + EXPOSE_SCREEN (s); + + if (es->grabIndex) + { + if (es->moreAdjust) + { + damageScreen (s); + } + else + { + if (es->state == EXPOSE_STATE_IN) + { + removeScreenGrab (s, es->grabIndex, 0); + es->grabIndex = 0; + } + else + es->state = EXPOSE_STATE_WAIT; + } + } + + UNWRAP (es, s, donePaintScreen); + (*s->donePaintScreen) (s); + WRAP (es, s, donePaintScreen, exposeDonePaintScreen); +} + +static CompWindow * +exposeCheckForWindowAt (CompScreen *s, + int x, + int y) +{ + int x1, y1, x2, y2; + CompWindow *w; + + for (w = s->reverseWindows; w; w = w->prev) + { + EXPOSE_WINDOW (w); + + if (ew->slot) + { + x1 = w->attrib.x + ew->tx; + y1 = w->attrib.y + ew->ty; + x2 = x1 + ((float) w->width * ew->scale); + y2 = y1 + ((float) w->height * ew->scale); + + if (x1 <= x && y1 <= y && x2 > x && y2 > y) + return w; + } + } + + return 0; +} + +static void +exposeInitiate (CompScreen *s) +{ + EXPOSE_SCREEN (s); + + if (layoutThumbs (s)) + { + if (!es->grabIndex) + es->grabIndex = pushScreenGrab (s, es->cursor); + + if (es->grabIndex) + { + damageScreen (s); + gettimeofday (&s->lastRedraw, 0); + es->state = EXPOSE_STATE_OUT; + } + } +} + +static void +exposeTerminate (CompScreen *s) +{ + EXPOSE_SCREEN (s); + + if (es->grabIndex) + { + CompWindow *w; + + for (w = s->windows; w; w = w->next) + { + EXPOSE_WINDOW (w); + + ew->slot = 0; + ew->adjust = TRUE; + } + + es->state = EXPOSE_STATE_IN; + + damageScreen (s); + } +} + +static Bool +exposeSelectWindow (CompWindow *w) +{ + EXPOSE_WINDOW (w); + + if (ew->slot && w->client && w->client != w->screen->activeWindow) + { + XClientMessageEvent cm; + + cm.type = ClientMessage; + cm.display = w->screen->display->display; + cm.format = 32; + + cm.message_type = w->screen->display->winActiveAtom; + cm.window = w->client; + + cm.data.l[0] = 2; + cm.data.l[1] = cm.data.l[2] = cm.data.l[3] = cm.data.l[4] = 0; + + XSendEvent (w->screen->display->display, w->screen->root, FALSE, + StructureNotifyMask, (XEvent *) &cm); + + return TRUE; + } + + return FALSE; +} + +static void +exposeSelectWindowAt (CompScreen *s, + int x, + int y) + +{ + CompWindow *w; + + w = exposeCheckForWindowAt (s, x, y); + if (w) + exposeSelectWindow (w); +} + +static void +exposeNextWindow (CompScreen *s) + +{ + CompWindow *w; + + for (w = s->windows; w; w = w->next) + if (s->activeWindow == w->client) + break; + + if (w) + { + for (w = w->next; w; w = w->next) + if (exposeSelectWindow (w)) + return; + + for (w = s->windows; w; w = w->next) + { + if (s->activeWindow == w->client) + break; + + if (exposeSelectWindow (w)) + return; + } + } +} + +static void +exposeHandleEvent (CompDisplay *d, + XEvent *event) +{ + CompScreen *s; + + EXPOSE_DISPLAY (d); + + switch (event->type) { + case KeyPress: + case KeyRelease: + s = findScreenAtDisplay (d, event->xkey.root); + if (s) + { + EXPOSE_SCREEN (s); + + if (EV_KEY (&es->opt[EXPOSE_SCREEN_OPTION_INITIATE], event)) + exposeInitiate (s); + + if (EV_KEY (&es->opt[EXPOSE_SCREEN_OPTION_NEXT_WINDOW], event)) + exposeNextWindow (s); + + if (EV_KEY (&es->opt[EXPOSE_SCREEN_OPTION_TERMINATE], event) || + (event->type == KeyPress && + event->xkey.keycode == s->escapeKeyCode)) + exposeTerminate (s); + } + break; + case ButtonPress: + case ButtonRelease: + s = findScreenAtDisplay (d, event->xbutton.root); + if (s) + { + EXPOSE_SCREEN (s); + + if (es->grabIndex && es->state != EXPOSE_STATE_IN) + exposeSelectWindowAt (s, + event->xbutton.x_root, + event->xbutton.y_root); + + if (EV_BUTTON (&es->opt[EXPOSE_SCREEN_OPTION_INITIATE], event)) + exposeInitiate (s); + + if (EV_BUTTON (&es->opt[EXPOSE_SCREEN_OPTION_NEXT_WINDOW], event)) + exposeNextWindow (s); + + if (EV_BUTTON (&es->opt[EXPOSE_SCREEN_OPTION_TERMINATE], event)) + exposeTerminate (s); + } + break; + case MotionNotify: + s = findScreenAtDisplay (d, event->xmotion.root); + if (s) + { + EXPOSE_SCREEN (s); + + if (es->grabIndex && + es->state != EXPOSE_STATE_IN && + es->opt[EXPOSE_SCREEN_OPTION_SLOPPY_FOCUS].value.b) + exposeSelectWindowAt (s, + event->xmotion.x_root, + event->xmotion.y_root); + } + default: + break; + } + + UNWRAP (ed, d, handleEvent); + (*d->handleEvent) (d, event); + WRAP (ed, d, handleEvent, exposeHandleEvent); + + switch (event->type) { + case CreateNotify: + case MapNotify: { + CompWindow *w; + + w = findWindowAtDisplay (d, (event->type == CreateNotify) ? + event->xcreatewindow.window : + event->xmap.window); + if (w && isExposeWin (w)) + { + EXPOSE_SCREEN (w->screen); + + if (es->grabIndex && layoutThumbs (w->screen)) + { + es->state = EXPOSE_STATE_OUT; + damageScreen (w->screen); + gettimeofday (&w->screen->lastRedraw, 0); + } + } + } + default: + break; + } +} + +static Bool +exposeInitDisplay (CompPlugin *p, + CompDisplay *d) +{ + ExposeDisplay *ed; + + ed = malloc (sizeof (ExposeDisplay)); + if (!ed) + return FALSE; + + ed->screenPrivateIndex = allocateScreenPrivateIndex (d); + if (ed->screenPrivateIndex < 0) + { + free (ed); + return FALSE; + } + + WRAP (ed, d, handleEvent, exposeHandleEvent); + + d->privates[displayPrivateIndex].ptr = ed; + + return TRUE; +} + +static void +exposeFiniDisplay (CompPlugin *p, + CompDisplay *d) +{ + EXPOSE_DISPLAY (d); + + freeScreenPrivateIndex (d, ed->screenPrivateIndex); + + UNWRAP (ed, d, handleEvent); + + free (ed); +} + +static Bool +exposeInitScreen (CompPlugin *p, + CompScreen *s) +{ + ExposeScreen *es; + + EXPOSE_DISPLAY (s->display); + + es = malloc (sizeof (ExposeScreen)); + if (!es) + return FALSE; + + es->windowPrivateIndex = allocateWindowPrivateIndex (s); + if (es->windowPrivateIndex < 0) + { + free (es); + return FALSE; + } + + es->grabIndex = 0; + + es->state = EXPOSE_STATE_NONE; + + es->slots = 0; + es->slotsSize = 0; + + es->windows = 0; + es->windowsSize = 0; + + es->line = 0; + es->lineSize = 0; + + es->scale = 1.0f; + + es->spacing = EXPOSE_SPACING_DEFAULT; + + exposeScreenInitOptions (es, s->display->display); + + addScreenBinding (s, &es->opt[EXPOSE_SCREEN_OPTION_INITIATE].value.bind); + + WRAP (es, s, preparePaintScreen, exposePreparePaintScreen); + WRAP (es, s, donePaintScreen, exposeDonePaintScreen); + WRAP (es, s, paintScreen, exposePaintScreen); + WRAP (es, s, paintWindow, exposePaintWindow); + + es->cursor = XCreateFontCursor (s->display->display, XC_left_ptr); + + s->privates[ed->screenPrivateIndex].ptr = es; + + return TRUE; +} + +static void +exposeFiniScreen (CompPlugin *p, + CompScreen *s) +{ + EXPOSE_SCREEN (s); + + UNWRAP (es, s, preparePaintScreen); + UNWRAP (es, s, donePaintScreen); + UNWRAP (es, s, paintScreen); + UNWRAP (es, s, paintWindow); + + if (es->slotsSize) + free (es->slots); + + if (es->lineSize) + free (es->line); + + if (es->windowsSize) + free (es->windows); + + free (es); +} + +static Bool +exposeInitWindow (CompPlugin *p, + CompWindow *w) +{ + ExposeWindow *ew; + + EXPOSE_SCREEN (w->screen); + + ew = malloc (sizeof (ExposeWindow)); + if (!ew) + return FALSE; + + ew->slot = 0; + ew->scale = 1.0f; + ew->tx = ew->ty = 0.0f; + ew->adjust = FALSE; + ew->xVelocity = ew->yVelocity = ew->scaleVelocity = 0.0f; + + w->privates[es->windowPrivateIndex].ptr = ew; + + return TRUE; +} + +static void +exposeFiniWindow (CompPlugin *p, + CompWindow *w) +{ + EXPOSE_WINDOW (w); + + free (ew); +} + +static Bool +exposeInit (CompPlugin *p) +{ + displayPrivateIndex = allocateDisplayPrivateIndex (); + if (displayPrivateIndex < 0) + return FALSE; + + return TRUE; +} + +static void +exposeFini (CompPlugin *p) +{ + if (displayPrivateIndex >= 0) + freeDisplayPrivateIndex (displayPrivateIndex); +} + +CompPluginVTable exposeVTable = { + "expose", + "Expose", + "Expose-like Window Switcher", + exposeInit, + exposeFini, + exposeInitDisplay, + exposeFiniDisplay, + exposeInitScreen, + exposeFiniScreen, + exposeInitWindow, + exposeFiniWindow, + 0, /* GetDisplayOptions */ + 0, /* SetDisplayOption */ + exposeGetScreenOptions, + exposeSetScreenOption +}; + +CompPluginVTable * +getCompPluginInfo (void) +{ + return &exposeVTable; +} diff --git a/plugins/fade.c b/plugins/fade.c new file mode 100644 index 0000000..86eb398 --- /dev/null +++ b/plugins/fade.c @@ -0,0 +1,437 @@ +/* + * 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 <comp.h> + +#define FADE_SPEED_DEFAULT 4.0f +#define FADE_SPEED_MIN 0.1f +#define FADE_SPEED_MAX 10.0f +#define FADE_SPEED_PRECISION 0.1f + +static int displayPrivateIndex; + +typedef struct _FadeDisplay { + int screenPrivateIndex; + HandleEventProc handleEvent; +} FadeDisplay; + +#define FADE_SCREEN_OPTION_FADE_SPEED 0 +#define FADE_SCREEN_OPTION_NUM 1 + +typedef struct _FadeScreen { + int windowPrivateIndex; + int fadeTime; + int steps; + + CompOption opt[FADE_SCREEN_OPTION_NUM]; + + PreparePaintScreenProc preparePaintScreen; + PaintWindowProc paintWindow; +} FadeScreen; + +typedef struct _FadeWindow { + int opacity; + int direction; + int destroyed; +} 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; + } + default: + break; + } + + return FALSE; +} + +static void +fadeScreenInitOptions (FadeScreen *fs) +{ + CompOption *o; + + 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; +} + +static void +fadePreparePaintScreen (CompScreen *s, + int msSinceLastPaint) +{ + FADE_SCREEN (s); + + fs->steps = (msSinceLastPaint * OPAQUE) / fs->fadeTime; + if (fs->steps < 256) + fs->steps = 256; + + 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 (fw->opacity < OPAQUE) + { + GLint opacity; + + opacity = fw->opacity + fs->steps * fw->direction; + if (opacity > 0) + { + if (opacity < OPAQUE) + { + WindowPaintAttrib fAttrib = *attrib; + + fAttrib.opacity = MULTIPLY_USHORT (opacity, attrib->opacity); + + UNWRAP (fs, s, paintWindow); + status = (*s->paintWindow) (w, &fAttrib, region, mask); + WRAP (fs, s, paintWindow, fadePaintWindow); + + if (status) + { + fw->opacity = opacity; + + addWindowDamage (w); + } + } + else + { + UNWRAP (fs, s, paintWindow); + status = (*s->paintWindow) (w, attrib, region, mask); + WRAP (fs, s, paintWindow, fadePaintWindow); + + if (status) + { + fw->opacity = OPAQUE; + fw->direction = 0; + } + } + } + else + { + fw->opacity = 0; + fw->direction = 0; + + if (fw->destroyed) + destroyWindow (w); + else + unmapWindow (w); + + 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 +fadeHandleEvent (CompDisplay *d, + XEvent *event) +{ + CompWindow *w; + + FADE_DISPLAY (d); + + switch (event->type) { + case DestroyNotify: + w = findWindowAtDisplay (d, event->xdestroywindow.window); + if (w) + { + FADE_WINDOW (w); + + if (!fw->direction) + fw->opacity = OPAQUE - 1; + + fw->direction = -1; + fw->destroyed = 1; + + addWindowDamage (w); + return; + } + break; + case UnmapNotify: + w = findWindowAtDisplay (d, event->xunmap.window); + if (w) + { + FADE_WINDOW (w); + + if (!fw->direction) + fw->opacity = OPAQUE - 1; + + fw->direction = -1; + + addWindowDamage (w); + return; + } + break; + case MapNotify: + w = findWindowAtDisplay (d, event->xmap.window); + if (w) + { + FADE_WINDOW (w); + + if (!fw->direction) + fw->opacity = 1; + + fw->direction = 1; + } + default: + break; + } + + UNWRAP (fd, d, handleEvent); + (*d->handleEvent) (d, event); + WRAP (fd, d, handleEvent, fadeHandleEvent); +} + +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; + } + + 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); + + 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); + + 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 = OPAQUE; + fw->direction = 0; + fw->destroyed = 0; + + w->privates[fs->windowPrivateIndex].ptr = fw; + + return TRUE; +} + +static void +fadeFiniWindow (CompPlugin *p, + CompWindow *w) +{ + FADE_WINDOW (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); +} + +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 +}; + +CompPluginVTable * +getCompPluginInfo (void) +{ + return &fadeVTable; +} diff --git a/plugins/gconf.c b/plugins/gconf.c new file mode 100644 index 0000000..9f0a342 --- /dev/null +++ b/plugins/gconf.c @@ -0,0 +1,1110 @@ +/* + * 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 <comp.h> + +#define APP_NAME "/apps/gnome-composite" +#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 }, + { "<NumLock>", CompNumLockMask }, + { "<ScrollLock>", CompScrollLockMask }, + { "<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); + + binding = g_strdup_printf ("%s%s", mods, + (keyname) ? keyname : "??"); + } + + 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, "value", NULL); + } + else + { + key = g_strjoin ("/", APP_NAME "/general", screen, "options", o->name, + "value", 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] * 256; + value->c[1] = c[1] * 256; + value->c[2] = c[2] * 256; + 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 && !isalpha (*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; + } + } + } + } + } + + return FALSE; +} + +static Bool +gconfGetOptionValue (CompDisplay *d, + GConfEntry *entry) +{ + CompPlugin *p = 0; + CompScreen *s = 0; + CompOption *o, *option; + gchar *ptr; + gchar *pluginPtr = 0, *optionPtr = 0; + gint pluginLen = 0, optionLen = 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; + + optionPtr = ptr; + ptr = strchr (ptr, '/'); + if (!ptr) + return FALSE; + + optionLen = ptr - optionPtr; + if (optionLen < 1) + return FALSE; + + if (strcmp (ptr, "/value") != 0) + 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); + } + + optionPtr = g_strndup (optionPtr, optionLen); + + o = compFindOption (option, nOption, optionPtr, 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, + optionPtr, + &value); + else + status = (*s->setScreenOption) (s, optionPtr, &value); + } + else + { + if (pluginPtr) + status = (*d->setDisplayOptionForPlugin) (d, + pluginPtr, + optionPtr, + &value); + else + status = (*d->setDisplayOption) (d, optionPtr, &value); + } + } + + if (o->type == CompOptionTypeList && + gvalue->type == GCONF_VALUE_LIST) + { + if (value.list.nValue && value.list.value) + free (value.list.value); + } + } + } + + g_free (optionPtr); + if (pluginPtr) + g_free (pluginPtr); + + return status; +} + +static void +gconfInitOption (CompDisplay *d, + CompOption *o, + gchar *screen, + gchar *plugin) +{ + GConfEntry *entry; + gchar *gconfpath, *key; + CompOptionType type; + + GCONF_DISPLAY (d); + + if (plugin) + { + gconfpath = g_strjoin ("/", APP_NAME "/plugins", plugin, screen, + "options", o->name, NULL); + } + else + { + gconfpath = g_strjoin ("/", APP_NAME "/general", screen, "options", + o->name, NULL); + } + + key = g_strconcat (gconfpath, "/short_description", NULL); + gconf_client_set_string (gd->client, key, o->shortDesc, NULL); + g_free (key); + + key = g_strconcat (gconfpath, "/long_description", NULL); + gconf_client_set_string (gd->client, key, o->longDesc, NULL); + g_free (key); + + key = g_strconcat (gconfpath, "/type", NULL); + + type = o->type; + if (type == CompOptionTypeList) + type = o->value.list.type; + + switch (type) { + case CompOptionTypeBool: + gconf_client_set_string (gd->client, key, "Bool", NULL); + break; + case CompOptionTypeInt: + gconf_client_set_string (gd->client, key, "Int", NULL); + break; + case CompOptionTypeFloat: + gconf_client_set_string (gd->client, key, "Float", NULL); + break; + case CompOptionTypeString: + gconf_client_set_string (gd->client, key, "String", NULL); + break; + case CompOptionTypeColor: + gconf_client_set_string (gd->client, key, "Color", NULL); + break; + case CompOptionTypeBinding: + gconf_client_set_string (gd->client, key, "Binding", NULL); + break; + default: + break; + } + + g_free (key); + key = g_strconcat (gconfpath, "/min", NULL); + + switch (type) { + case CompOptionTypeInt: + gconf_client_set_int (gd->client, key, o->rest.i.min, NULL); + break; + case CompOptionTypeFloat: + gconf_client_set_float (gd->client, key, o->rest.f.min, NULL); + break; + default: + break; + } + + g_free (key); + key = g_strconcat (gconfpath, "/max", NULL); + + switch (type) { + case CompOptionTypeInt: + gconf_client_set_int (gd->client, key, o->rest.i.max, NULL); + break; + case CompOptionTypeFloat: + gconf_client_set_float (gd->client, key, o->rest.f.max, NULL); + break; + default: + break; + } + + g_free (key); + key = g_strconcat (gconfpath, "/precision", NULL); + + switch (type) { + case CompOptionTypeFloat: + gconf_client_set_float (gd->client, key, o->rest.f.precision, NULL); + break; + default: + break; + } + + g_free (key); + key = g_strconcat (gconfpath, "/choices", NULL); + + switch (type) { + case CompOptionTypeString: { + GSList *list = NULL; + int i; + + for (i = 0; i < o->rest.s.nString; i++) + list = g_slist_append (list, o->rest.s.string[i]); + + gconf_client_set_list (gd->client, key, GCONF_VALUE_STRING, list, + NULL); + + g_slist_free (list); + } break; + default: + break; + } + + g_free (key); + key = g_strconcat (gconfpath, "/value", 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); + g_free (gconfpath); +} + +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; + } + + 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); + + return TRUE; +} + +static void +gconfFiniScreen (CompPlugin *p, + CompScreen *s) +{ + GCONF_SCREEN (s); + + 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); +} + +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 */ +}; + +CompPluginVTable * +getCompPluginInfo (void) +{ + return &gconfVTable; +} diff --git a/plugins/rotate.c b/plugins/rotate.c new file mode 100644 index 0000000..1202227 --- /dev/null +++ b/plugins/rotate.c @@ -0,0 +1,807 @@ +/* + * 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 <comp.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 | CompSuperMask) + +#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 | CompSuperMask) + +#define ROTATE_RIGHT_KEY_DEFAULT "Right" +#define ROTATE_RIGHT_MODIFIERS_DEFAULT \ + (CompPressMask | ControlMask | CompSuperMask) + +#define ROTATE_SNAP_TOP_DEFAULT FALSE + +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_SNAP_TOP 7 +#define ROTATE_SCREEN_OPTION_NUM 8 + +typedef struct _RotateScreen { + PreparePaintScreenProc preparePaintScreen; + DonePaintScreenProc donePaintScreen; + PaintScreenProc paintScreen; + + CompOption opt[ROTATE_SCREEN_OPTION_NUM]; + + Bool pointerInvertY; + float pointerSensitivity; + Bool snapTop; + float acceleration; + + int grabIndex; + + GLfloat xrot, xVelocity; + GLfloat yrot, yVelocity; + + GLfloat baseXrot; + + Bool moving; + GLfloat moveTo; + + 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: + 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; + } + 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_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; +} + +static int +adjustVelocity (RotateScreen *rs) +{ + float xrot, yrot, adjust, amount; + + if (rs->moving) + { + xrot = rs->moveTo + (rs->xrot + rs->baseXrot); + } + else + { + xrot = rs->xrot; + if (rs->xrot < -45.0f) + xrot = 90.0f + rs->xrot; + else if (rs->xrot > 45.0f) + xrot = rs->xrot - 90.0f; + } + + 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) + { + rs->xrot += (rs->xVelocity * msSinceLastPaint) / s->redrawTime; + rs->yrot += (rs->yVelocity * msSinceLastPaint) / s->redrawTime; + + if (rs->xrot > 90.0f) + { + rs->baseXrot += 90.0f; + rs->xrot -= 90.0f; + } + else if (rs->xrot < 0.0f) + { + rs->baseXrot -= 90.0f; + rs->xrot += 90.0f; + } + + if (rs->yrot > 100.0f) + { + rs->yVelocity = -0.5f; + rs->yrot = 100.0f; + } + else if (rs->yrot < -100.0f) + { + rs->yVelocity = 0.5f; + 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)) + { + rs->xVelocity = 0.0f; + rs->yVelocity = 0.0f; + + if (fabs (rs->yrot) < 0.1f) + { + int move; + + rs->xrot += rs->baseXrot; + if (rs->xrot < 0.0f) + move = (rs->xrot / 90.0f) - 0.5f; + else + move = (rs->xrot / 90.0f) + 0.5f; + + if (move) + { + CompWindow *w; + + for (w = s->windows; w; w = w->next) + { + if (w->attrib.map_state != IsViewable) + continue; + + if (w->type == s->display->winDesktopAtom || + w->type == s->display->winDockAtom) + continue; + + (*s->invisibleWindowMove) (w, s->width * move, 0); + + w->invisible = WINDOW_INVISIBLE (w); + + XMoveWindow (s->display->display, + (w->client) ? w->client : w->id, + w->attrib.x, w->attrib.y); + } + } + + 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; + } + } + } + + 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, + const WindowPaintAttrib *wAttrib, + 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, wAttrib, region, mask); + WRAP (rs, s, paintScreen, rotatePaintScreen); + } + else + { + UNWRAP (rs, s, paintScreen); + status = (*s->paintScreen) (s, sAttrib, wAttrib, 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; + + if (!rs->grabIndex) + { + rs->grabIndex = pushScreenGrab (s, s->invisibleCursor); + if (rs->grabIndex) + { + rs->savedPointer.x = rs->prevPointerX; + rs->savedPointer.y = rs->prevPointerY; + + gettimeofday (&s->lastRedraw, 0); + } + } + + if (rs->grabIndex) + { + rs->grabbed = TRUE; + rs->snapTop = rs->opt[ROTATE_SCREEN_OPTION_SNAP_TOP].value.b; + } +} + +static void +rotateLeft (CompScreen *s, + int x, + int y) +{ + ROTATE_SCREEN (s); + + if (!rs->grabIndex) + rotateInitiate (s, x, y); + + if (rs->grabIndex) + { + rs->moving = TRUE; + rs->moveTo -= 90.0f; + rs->grabbed = FALSE; + damageScreen (s); + } +} + +static void +rotateRight (CompScreen *s, + int x, + int y) +{ + ROTATE_SCREEN (s); + + if (!rs->grabIndex) + rotateInitiate (s, x, y); + + if (rs->grabIndex) + { + rs->moving = TRUE; + rs->moveTo += 90.0f; + 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], event)) + rotateLeft (s, event->xkey.x_root, event->xkey.y_root); + + if (EV_KEY (&rs->opt[ROTATE_SCREEN_OPTION_RIGHT], event)) + rotateRight (s, event->xkey.x_root, event->xkey.y_root); + + 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], event)) + rotateLeft (s, event->xbutton.x_root, event->xbutton.y_root); + + if (EV_BUTTON (&rs->opt[ROTATE_SCREEN_OPTION_RIGHT], event)) + rotateRight (s, event->xbutton.x_root, event->xbutton.y_root); + + 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->yVelocity += pointerDy * rs->pointerSensitivity; + + damageScreen (s); + return; + } + } + default: + break; + } + + UNWRAP (rd, d, handleEvent); + (*d->handleEvent) (d, event); + WRAP (rd, d, handleEvent, rotateHandleEvent); +} + +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->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; + + 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); + + WRAP (rs, s, preparePaintScreen, rotatePreparePaintScreen); + WRAP (rs, s, donePaintScreen, rotateDonePaintScreen); + WRAP (rs, s, paintScreen, rotatePaintScreen); + + s->privates[rd->screenPrivateIndex].ptr = rs; + + return TRUE; +} + +static void +rotateFiniScreen (CompPlugin *p, + CompScreen *s) +{ + ROTATE_SCREEN (s); + + UNWRAP (rs, s, preparePaintScreen); + UNWRAP (rs, s, donePaintScreen); + UNWRAP (rs, s, paintScreen); + + free (rs); +} + +static Bool +rotateInit (CompPlugin *p) +{ + if (!findActivePlugin ("cube")) + { + fprintf (stderr, "%s: 'cube' required but not loaded\n", programName); + return FALSE; + } + + displayPrivateIndex = allocateDisplayPrivateIndex (); + if (displayPrivateIndex < 0) + return FALSE; + + return TRUE; +} + +static void +rotateFini (CompPlugin *p) +{ + if (displayPrivateIndex >= 0) + freeDisplayPrivateIndex (displayPrivateIndex); +} + +CompPluginVTable rotateVTable = { + "rotate", + "Rotate Cube", + "Rotate desktop cube", + rotateInit, + rotateFini, + rotateInitDisplay, + rotateFiniDisplay, + rotateInitScreen, + rotateFiniScreen, + 0, /* InitWindow */ + 0, /* FiniWindow */ + 0, /* GetDisplayOptions */ + 0, /* SetDisplayOption */ + rotateGetScreenOptions, + rotateSetScreenOption +}; + +CompPluginVTable * +getCompPluginInfo (void) +{ + return &rotateVTable; +} diff --git a/plugins/wobbly.c b/plugins/wobbly.c new file mode 100644 index 0000000..6a18845 --- /dev/null +++ b/plugins/wobbly.c @@ -0,0 +1,1496 @@ +/* + * 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 <comp.h> + +#define GRID_WIDTH 4 +#define GRID_HEIGHT 4 + +#define MODEL_MAX_SPRINGS (GRID_WIDTH * GRID_HEIGHT * 2) + +typedef struct _xy_pair { + float x, y; +} Point, Vector; + +typedef struct _Object { + Vector force; + Point position; + Vector velocity; + float theta; + Bool immobile; +} Object; + +typedef struct _Spring { + Object *a; + Object *b; + Vector offset; +} Spring; + +typedef struct _Model { + Object *objects; + int numObjects; + Spring springs[MODEL_MAX_SPRINGS]; + int numSprings; + Object *anchorObject; + float steps; + Vector translate; + Vector scale; +} Model; + +#define WOBBLY_FRICTION_DEFAULT 2.5f +#define WOBBLY_FRICTION_MIN 0.1f +#define WOBBLY_FRICTION_MAX 10.0f +#define WOBBLY_FRICTION_PRECISION 0.1f + +#define WOBBLY_SPRING_K_DEFAULT 3.5f +#define WOBBLY_SPRING_K_MIN 0.1f +#define WOBBLY_SPRING_K_MAX 10.0f +#define WOBBLY_SPRING_K_PRECISION 0.1f + +#define WOBBLY_GRID_SIZE_DEFAULT 64 +#define WOBBLY_GRID_SIZE_MIN 16 +#define WOBBLY_GRID_SIZE_MAX 512 + +typedef enum { + WobblyEffectNone = 0, + WobblyEffectExplode, + WobblyEffectShiver +} WobblyEffect; + +static char *effectName[] = { + "None", + "Explode", + "Shiver" +}; + +static WobblyEffect effectType[] = { + WobblyEffectNone, + WobblyEffectExplode, + WobblyEffectShiver +}; + +#define NUM_EFFECT (sizeof (effectType) / sizeof (effectType[0])) + +#define WOBBLY_MAP_DEFAULT (effectName[0]) +#define WOBBLY_FOCUS_DEFAULT (effectName[0]) + +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_SIZE 2 +#define WOBBLY_SCREEN_OPTION_MAP_EFFECT 3 +#define WOBBLY_SCREEN_OPTION_FOCUS_EFFECT 4 +#define WOBBLY_SCREEN_OPTION_NUM 5 + +typedef struct _WobblyScreen { + int windowPrivateIndex; + PreparePaintScreenProc preparePaintScreen; + DonePaintScreenProc donePaintScreen; + PaintScreenProc paintScreen; + PaintWindowProc paintWindow; + InvisibleWindowMoveProc invisibleWindowMove; + + CompOption opt[WOBBLY_SCREEN_OPTION_NUM]; + + Bool wobblyWindows; + + WobblyEffect mapEffect; + WobblyEffect focusEffect; +} WobblyScreen; + +typedef struct _WobblyWindow { + Model *model; + Bool wobbly; + GLfloat *vertices; + int vertexSize; + GLushort *indices; + int indexSize; +} 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_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; + } + } + } + default: + break; + } + + return FALSE; +} + +static void +wobblyScreenInitOptions (WobblyScreen *ws) +{ + CompOption *o; + + 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_SIZE]; + o->name = "grid_size"; + o->shortDesc = "Grid Size"; + o->longDesc = "Vertex Grid Resolution"; + o->type = CompOptionTypeInt; + o->value.i = WOBBLY_GRID_SIZE_DEFAULT; + o->rest.i.min = WOBBLY_GRID_SIZE_MIN; + o->rest.i.max = WOBBLY_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; +} + +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; +} + +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 +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; + x += model->translate.x; + y += model->translate.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; + x += model->translate.x; + y += model->translate.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 +modelAdjustObjectsForExplosion (Model *model, + int x, + int y, + int width, + int height) +{ + int gridX, gridY, i = 0; + int vX, vY; + float scale; + float w, h; + + w = (float) width * model->scale.x; + h = (float) height * model->scale.y; + x += model->translate.x; + y += model->translate.y; + + for (gridY = 0; gridY < GRID_HEIGHT; gridY++) + { + for (gridX = 0; gridX < GRID_WIDTH; gridX++) + { + if (!model->objects[i].immobile) + { + scale = ((float) rand () * 0.1f) / RAND_MAX; + vX = (model->objects[i].position.x - (x + w / 2.0f)) * scale; + vY = (model->objects[i].position.y - (y + h / 2.0f)) * scale; + + model->objects[i].position.x = x + w / 2.0f; + model->objects[i].position.y = y + h / 2.0f; + + model->objects[i].velocity.x += vX; + model->objects[i].velocity.y += vY; + } + + i++; + } + } +} + +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; + x += model->translate.x; + y += model->translate.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 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++) + { + for (gridX = 0; gridX < GRID_WIDTH; gridX++) + { + if (gridX > 0) + modelAddSpring (model, + &model->objects[i - 1], + &model->objects[i], + hpad, 0); + + if (gridY > 0) + modelAddSpring (model, + &model->objects[i - GRID_WIDTH], + &model->objects[i], + 0, vpad); + + i++; + } + } +} + +static void +modelMove (Model *model, + int dx, + int dy) +{ + int i; + + for (i = 0; i < model->numObjects; i++) + { + model->objects[i].position.x += dx; + model->objects[i].position.y += dy; + } +} + +static Model * +createModel (int x, + int y, + int width, + int height) +{ + 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->translate.x = 0.0f; + model->translate.y = 0.0f; + + model->scale.x = 1.0f; + model->scale.y = 1.0f; + + modelInitObjects (model, x, y, width, height); + modelInitSprings (model, x, y, width, height); + + 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 float +modelStepObject (Model *model, + Object *object, + float friction) +{ + object->theta += 0.05f; + + object->force.x -= friction * object->velocity.x; + object->force.y -= friction * object->velocity.y; + + if (object->immobile) + { + object->velocity.x = 0.0f; + object->velocity.y = 0.0f; + } + else + { + object->velocity.x += object->force.x / 20.0f; + object->velocity.y += object->force.y / 20.0f; + + object->position.x += object->velocity.x; + object->position.y += object->velocity.y; + } + + object->force.x = 0.0f; + object->force.y = 0.0f; + + return fabs (object->velocity.x) + fabs (object->velocity.y); +} + +static Bool +modelStep (Model *model, + float friction, + float k, + float time) +{ + int i, j, steps; + float velocitySum = 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 (model, + &model->objects[i], + friction); + } + + return (velocitySum > 0.5f); +} + +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) + { + ww->model = createModel (w->attrib.x, w->attrib.y, + w->width, w->height); + 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 void +wobblyMoveWindow (CompWindow *w, + int dx, + int dy) +{ + WOBBLY_WINDOW (w); + + if (!wobblyEnsureModel (w)) + return; + + if (!ww->wobbly) + { + Window win; + int i, x, y; + unsigned int ui; + + WOBBLY_SCREEN (w->screen); + + XQueryPointer (w->screen->display->display, w->screen->root, + &win, &win, &x, &y, &i, &i, &ui); + + ww->model->anchorObject->immobile = FALSE; + + if (x < w->attrib.x || x > w->attrib.x + w->width || + y < w->attrib.y || y > w->attrib.y + w->height) + { + x = w->attrib.x + w->width / 2; + y = w->attrib.y + w->height / 2; + } + + ww->model->anchorObject = modelFindNearestObject (ww->model, x, y); + ww->model->anchorObject->immobile = TRUE; + + ww->wobbly = ws->wobblyWindows = TRUE; + } + + ww->model->anchorObject->position.x += dx; + ww->model->anchorObject->position.y += dy; + + damageScreen (w->screen); +} + +static Bool +isWobblyWin (CompWindow *w) +{ + if (w->type == w->screen->display->winDesktopAtom || + w->type == w->screen->display->winDockAtom) + return FALSE; + + return TRUE; +} + +static void +wobblyPreparePaintScreen (CompScreen *s, + int msSinceLastPaint) +{ + WobblyWindow *ww; + CompWindow *w; + + WOBBLY_SCREEN (s); + + if (ws->wobblyWindows) + { + float friction, springK; + + friction = ws->opt[WOBBLY_SCREEN_OPTION_FRICTION].value.f; + springK = ws->opt[WOBBLY_SCREEN_OPTION_SPRING_K].value.f; + + ws->wobblyWindows = FALSE; + for (w = s->windows; w; w = w->next) + { + ww = GET_WOBBLY_WINDOW (w, ws); + + if (ww->wobbly) + { + if (w->attrib.map_state == IsViewable && + modelStep (ww->model, friction, springK, msSinceLastPaint)) + { + ws->wobblyWindows = TRUE; + } + else + { + modelSetMiddleAnchor (ww->model, + w->attrib.x, w->attrib.y, + w->width, w->height); + ww->wobbly = FALSE; + } + } + } + } + + UNWRAP (ws, s, preparePaintScreen); + (*s->preparePaintScreen) (s, msSinceLastPaint); + WRAP (ws, s, preparePaintScreen, wobblyPreparePaintScreen); +} + +static void +wobblyDonePaintScreen (CompScreen *s) +{ + WOBBLY_SCREEN (s); + + if (ws->wobblyWindows) + damageScreen (s); + + UNWRAP (ws, s, donePaintScreen); + (*s->donePaintScreen) (s); + WRAP (ws, s, donePaintScreen, wobblyDonePaintScreen); +} + +static void +wobblyTransformWindow (CompWindow *w, + const WindowPaintAttrib *attrib) +{ + WOBBLY_WINDOW (w); + WOBBLY_SCREEN (w->screen); + + if (wobblyEnsureModel (w)) + { + modelSetMiddleAnchor (ww->model, + w->attrib.x, w->attrib.y, + w->width, w->height); + + if (ww->model->scale.x != attrib->xScale || + ww->model->scale.y != attrib->yScale) + { + ww->model->scale.x = attrib->xScale; + ww->model->scale.y = attrib->yScale; + + modelInitSprings (ww->model, + w->attrib.x, w->attrib.y, + w->width, w->height); + + ww->wobbly = ws->wobblyWindows = TRUE; + } + + if (ww->model->translate.x != attrib->xTranslate || + ww->model->translate.y != attrib->yTranslate) + { + ww->model->anchorObject->position.x += + attrib->xTranslate - ww->model->translate.x; + ww->model->anchorObject->position.y += + attrib->yTranslate - ww->model->translate.y; + + ww->model->translate.x = attrib->xTranslate; + ww->model->translate.y = attrib->yTranslate; + + ww->wobbly = ws->wobblyWindows = TRUE; + } + } +} + +static Bool +wobblyPaintWindow (CompWindow *w, + const WindowPaintAttrib *attrib, + Region region, + unsigned int mask) +{ + BoxPtr pClip; + int nClip, nVertices, nIndices; + GLushort *i, *indices; + GLfloat *v, *vertices; + int x1, y1, x2, y2; + float width, height; + float deformedX, deformedY; + int x, y, iw, ih; + + WOBBLY_WINDOW (w); + WOBBLY_SCREEN (w->screen); + + if (ww->model) + { + if (attrib->xTranslate != ww->model->translate.x || + attrib->yTranslate != ww->model->translate.y || + attrib->xScale != ww->model->scale.x || + attrib->yScale != ww->model->scale.y) + wobblyTransformWindow (w, attrib); + } + else + { + if (attrib->xTranslate != 0.0f || + attrib->yTranslate != 0.0f || + attrib->xScale != 1.0f || + attrib->yScale != 1.0f) + { + if (isWobblyWin (w)) + wobblyTransformWindow (w, attrib); + } + } + + if (ww->wobbly) + { + GLushort opacity; + int gridSize; + + if (!region->numRects) + return TRUE; + + if (w->destroyed || w->attrib.map_state != IsViewable) + return TRUE; + + if (mask & PAINT_WINDOW_SOLID_MASK) + { + if (w->alpha) + return FALSE; + + opacity = MULTIPLY_USHORT (w->opacity, attrib->opacity); + if (opacity != OPAQUE) + return FALSE; + } + else if (mask & PAINT_WINDOW_TRANSLUCENT_MASK) + { + opacity = MULTIPLY_USHORT (w->opacity, attrib->opacity); + if (!w->alpha && opacity == OPAQUE) + return FALSE; + } + else + { + opacity = MULTIPLY_USHORT (w->opacity, attrib->opacity); + if (w->alpha || opacity != OPAQUE) + mask |= PAINT_WINDOW_TRANSLUCENT_MASK; + else + mask |= PAINT_WINDOW_SOLID_MASK; + } + + gridSize = ws->opt[WOBBLY_SCREEN_OPTION_GRID_SIZE].value.i; + + if (!w->pixmap) + bindWindow (w); + + width = w->width; + height = w->height; + + nClip = w->region->numRects; + pClip = w->region->rects; + + nVertices = 0; + nIndices = 0; + + i = ww->indices; + v = ww->vertices; + + while (nClip--) + { + x1 = pClip->x1 - w->attrib.x; + y1 = pClip->y1 - w->attrib.y; + x2 = pClip->x2 - w->attrib.x; + y2 = pClip->y2 - w->attrib.y; + + iw = ((x2 - x1 - 1) / gridSize) + 1; + ih = ((y2 - y1 - 1) / gridSize) + 1; + + if (nIndices + (iw * ih * 4) > ww->indexSize) + { + indices = realloc (ww->indices, sizeof (GLushort) * + (ww->indexSize + (iw * ih * 4))); + if (!indices) + return FALSE; + + ww->indices = indices; + ww->indexSize += iw * ih * 4; + i = ww->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) * 4) > ww->vertexSize) + { + vertices = realloc (ww->vertices, + sizeof (GLfloat) * + (ww->vertexSize + (iw * ih * 4))); + if (!vertices) + return FALSE; + + ww->vertices = vertices; + ww->vertexSize += iw * ih * 4; + v = ww->vertices + (nVertices * 4); + } + + for (y = y1;; y += gridSize) + { + if (y > y2) + y = y2; + + for (x = x1;; x += gridSize) + { + if (x > x2) + x = x2; + + bezierPatchEvaluate (ww->model, + x / width, y / height, + &deformedX, &deformedY); + + *v++ = X_WINDOW_TO_TEXTURE_SPACE (w, x); + *v++ = Y_WINDOW_TO_TEXTURE_SPACE (w, y); + *v++ = deformedX; + *v++ = deformedY; + + nVertices++; + + if (x == x2) + break; + } + + if (y == y2) + break; + } + + pClip++; + } + + glTexCoordPointer (2, GL_FLOAT, sizeof (GLfloat) * 4, ww->vertices); + glVertexPointer (2, GL_FLOAT, sizeof (GLfloat) * 4, ww->vertices + 2); + + if (mask & PAINT_WINDOW_TRANSLUCENT_MASK) + { + glEnable (GL_BLEND); + if (opacity != OPAQUE) + { + glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glColor4us (opacity, opacity, opacity, opacity); + } + } + + enableTexture (w->screen, &w->texture, COMP_TEXTURE_FILTER_GOOD); + glDrawElements (GL_QUADS, nIndices, GL_UNSIGNED_SHORT, ww->indices); + disableTexture (&w->texture); + + if (mask & PAINT_WINDOW_TRANSLUCENT_MASK) + { + if (opacity != OPAQUE) + { + glColor4usv (defaultColor); + glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + } + glDisable (GL_BLEND); + } + } + else + { + Bool status; + + WOBBLY_SCREEN (w->screen); + + UNWRAP (ws, w->screen, paintWindow); + status = (*w->screen->paintWindow) (w, attrib, region, mask); + WRAP (ws, w->screen, paintWindow, wobblyPaintWindow); + + return status; + } + + return TRUE; +} + +static void +wobblyHandleEvent (CompDisplay *d, + XEvent *event) +{ + Window activeWindow = 0; + CompWindow *w; + + WOBBLY_DISPLAY (d); + + switch (event->type) { + case MapNotify: + w = findWindowAtDisplay (d, event->xmap.window); + if (w && isWobblyWin (w)) + { + WOBBLY_WINDOW (w); + WOBBLY_SCREEN (w->screen); + + if (ws->mapEffect && wobblyEnsureModel (w)) + { + switch (ws->mapEffect) { + case WobblyEffectExplode: + modelAdjustObjectsForExplosion (ww->model, + w->attrib.x, w->attrib.y, + w->width, w->height); + break; + case WobblyEffectShiver: + modelAdjustObjectsForShiver (ww->model, + w->attrib.x, w->attrib.y, + w->width, w->height); + default: + break; + } + + ww->wobbly = ws->wobblyWindows = TRUE; + damageScreen (w->screen); + } + } + break; + case ConfigureNotify: + w = findWindowAtDisplay (d, event->xmap.window); + if (w && isWobblyWin (w)) + { + if (w->attrib.width != event->xconfigure.width || + w->attrib.height != event->xconfigure.height || + w->attrib.border_width != event->xconfigure.border_width) + { + int width, height; + + WOBBLY_WINDOW (w); + WOBBLY_SCREEN (w->screen); + + width = event->xconfigure.width; + width += event->xconfigure.border_width * 2; + + height = event->xconfigure.height; + height += event->xconfigure.border_width * 2; + + if (w->attrib.map_state == IsViewable && wobblyEnsureModel (w)) + { + modelSetMiddleAnchor (ww->model, + w->attrib.x, w->attrib.y, + width, height); + + modelInitSprings (ww->model, + w->attrib.x, w->attrib.y, + width, height); + + ww->wobbly = ws->wobblyWindows = TRUE; + damageScreen (w->screen); + } + else if (ww->model) + { + modelInitObjects (ww->model, + w->attrib.x, w->attrib.y, + width, height); + + modelInitSprings (ww->model, + w->attrib.x, w->attrib.y, + width, height); + } + } + + if (w->attrib.x != event->xconfigure.x || + w->attrib.y != event->xconfigure.y) + { + WOBBLY_WINDOW (w); + + if (w->attrib.map_state == IsViewable && wobblyEnsureModel (w)) + { + wobblyMoveWindow (w, + event->xconfigure.x - w->attrib.x, + event->xconfigure.y - w->attrib.y); + } + else if (ww->model) + { + modelMove (ww->model, + event->xconfigure.x - w->attrib.x, + event->xconfigure.y - w->attrib.y); + } + } + } + break; + case PropertyNotify: + if (event->xproperty.atom == d->winActiveAtom) + { + CompScreen *s; + + s = findScreenAtDisplay (d, event->xproperty.window); + if (s) + activeWindow = s->activeWindow; + } + 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) + { + CompScreen *s; + + s = findScreenAtDisplay (d, event->xproperty.window); + if (s && s->activeWindow != activeWindow) + { + CompWindow *w; + + w = findClientWindowAtScreen (s, s->activeWindow); + if (w && isWobblyWin (w)) + { + WOBBLY_WINDOW (w); + WOBBLY_SCREEN (w->screen); + + if (ws->focusEffect && wobblyEnsureModel (w)) + { + switch (ws->focusEffect) { + case WobblyEffectExplode: + modelAdjustObjectsForExplosion (ww->model, + w->attrib.x, + w->attrib.y, + w->width, + w->height); + break; + case WobblyEffectShiver: + modelAdjustObjectsForShiver (ww->model, + w->attrib.x, + w->attrib.y, + w->width, + w->height); + default: + break; + } + + ww->wobbly = ws->wobblyWindows = TRUE; + damageScreen (w->screen); + } + } + } + } + default: + break; + } +} + +static Bool +wobblyPaintScreen (CompScreen *s, + const ScreenPaintAttrib *sAttrib, + const WindowPaintAttrib *wAttrib, + Region region, + unsigned int mask) +{ + Bool status; + + WOBBLY_SCREEN (s); + + if (ws->wobblyWindows) + { + mask &= ~PAINT_SCREEN_REGION_MASK; + mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS_MASK; + } + + UNWRAP (ws, s, paintScreen); + status = (*s->paintScreen) (s, sAttrib, wAttrib, region, mask); + WRAP (ws, s, paintScreen, wobblyPaintScreen); + + return status; +} + +static void +wobblyInvisibleWindowMove (CompWindow *w, + int dx, + int dy) +{ + WOBBLY_SCREEN (w->screen); + WOBBLY_WINDOW (w); + + if (ww->model) + modelMove (ww->model, dx, dy); + + UNWRAP (ws, w->screen, invisibleWindowMove); + (*w->screen->invisibleWindowMove) (w, dx, dy); + WRAP (ws, w->screen, invisibleWindowMove, wobblyInvisibleWindowMove); +} + +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 = WobblyEffectShiver; + + wobblyScreenInitOptions (ws); + + WRAP (ws, s, preparePaintScreen, wobblyPreparePaintScreen); + WRAP (ws, s, donePaintScreen, wobblyDonePaintScreen); + WRAP (ws, s, paintScreen, wobblyPaintScreen); + WRAP (ws, s, paintWindow, wobblyPaintWindow); + WRAP (ws, s, invisibleWindowMove, wobblyInvisibleWindowMove); + + 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, invisibleWindowMove); + + free (ws); +} + +static Bool +wobblyInitWindow (CompPlugin *p, + CompWindow *w) +{ + WobblyWindow *ww; + + WOBBLY_SCREEN (w->screen); + + ww = malloc (sizeof (WobblyWindow)); + if (!ww) + return FALSE; + + ww->vertices = 0; + ww->vertexSize = 0; + + ww->indices = 0; + ww->indexSize = 0; + + ww->model = 0; + ww->wobbly = FALSE; + + w->privates[ws->windowPrivateIndex].ptr = ww; + + return TRUE; +} + +static void +wobblyFiniWindow (CompPlugin *p, + CompWindow *w) +{ + WOBBLY_WINDOW (w); + + if (ww->vertices) + free (ww->vertices); + + if (ww->indices) + free (ww->indices); + + 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); +} + +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 +}; + +CompPluginVTable * +getCompPluginInfo (void) +{ + return &wobblyVTable; +} diff --git a/plugins/zoom.c b/plugins/zoom.c new file mode 100644 index 0000000..94bc4f7 --- /dev/null +++ b/plugins/zoom.c @@ -0,0 +1,690 @@ +/* + * 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 <comp.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) + +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_NUM 6 + +typedef struct _ZoomScreen { + PreparePaintScreenProc preparePaintScreen; + DonePaintScreenProc donePaintScreen; + PaintScreenProc paintScreen; + + CompOption opt[ZOOM_SCREEN_OPTION_NUM]; + + Bool pointerInvertY; + float pointerSensitivity; + + 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; +} 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; + 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; +} + +#define MIN_Z (0.6f) +#define MAX_ZOOM (MIN_Z / BASE_Z_TRANSLATE) + +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) + { + 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 * msSinceLastPaint) / s->redrawTime; + zs->yTranslate += (zs->yVelocity * msSinceLastPaint) / s->redrawTime; + + if (adjustZoomVelocity (zs)) + { + zs->currentZoom = zs->newZoom; + zs->zVelocity = 0.0f; + } + else + { + zs->currentZoom += (zs->zVelocity * msSinceLastPaint) / + s->redrawTime; + } + + zs->ztrans = BASE_Z_TRANSLATE * zs->currentZoom; + if (zs->ztrans < MIN_Z) + { + zs->zVelocity = 0.0f; + zs->ztrans = MIN_Z; + } + zs->ztrans -= BASE_Z_TRANSLATE; + + 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; + } + } + } + + 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, + const WindowPaintAttrib *wAttrib, + 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.zTranslate -= 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, wAttrib, region, mask); + WRAP (zs, s, paintScreen, zoomPaintScreen); + } + else + { + UNWRAP (zs, s, paintScreen); + status = (*s->paintScreen) (s, sAttrib, wAttrib, region, mask); + WRAP (zs, s, paintScreen, zoomPaintScreen); + } + + return status; +} + +static void +zoomIn (CompScreen *s, + int x, + int y) +{ + ZOOM_SCREEN (s); + + 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; + + if (zs->newZoom > MAX_ZOOM + 0.01f) + { + zs->newZoom = MAX_ZOOM + (zs->newZoom - MAX_ZOOM) / 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; + } + + gettimeofday (&s->lastRedraw, 0); + } +} + +static void +zoomOut (CompScreen *s) +{ + ZOOM_SCREEN (s); + + if (zs->grabIndex) + { + zs->newZoom = MAX_ZOOM + (zs->newZoom - MAX_ZOOM) * 2.0f; + if (zs->newZoom > 0.9f) + { + 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 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->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; + + 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); + + s->privates[zd->screenPrivateIndex].ptr = zs; + + return TRUE; +} + +static void +zoomFiniScreen (CompPlugin *p, + CompScreen *s) +{ + ZOOM_SCREEN (s); + + UNWRAP (zs, s, preparePaintScreen); + UNWRAP (zs, s, donePaintScreen); + UNWRAP (zs, s, paintScreen); + + free (zs); +} + +static Bool +zoomInit (CompPlugin *p) +{ + if (!findActivePlugin ("cube")) + { + fprintf (stderr, "%s: 'cube' required but not loaded\n", programName); + return FALSE; + } + + displayPrivateIndex = allocateDisplayPrivateIndex (); + if (displayPrivateIndex < 0) + return FALSE; + + return TRUE; +} + +static void +zoomFini (CompPlugin *p) +{ + if (displayPrivateIndex >= 0) + freeDisplayPrivateIndex (displayPrivateIndex); +} + +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 +}; + +CompPluginVTable * +getCompPluginInfo (void) +{ + return &zoomVTable; +} diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..2705fb3 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,22 @@ +INCLUDES = \ + @GLXCOMP_CFLAGS@ \ + -I$(top_srcdir)/include \ + -DPLUGINDIR=\"$(plugindir)\" \ + -DIMAGEDIR=\"$(imagedir)\" + +bin_PROGRAMS = glxcompmgr + +glxcompmgr_LDADD = @GLXCOMP_LIBS@ @GL_LIBS@ -lm +glxcompmgr_LDFLAGS = -export-dynamic +glxcompmgr_SOURCES = \ + glxcompmgr.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/display.c b/src/display.c new file mode 100644 index 0000000..69d01b0 --- /dev/null +++ b/src/display.c @@ -0,0 +1,1149 @@ +/* + * 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 <sys/poll.h> +#include <unistd.h> + +#define XK_MISCELLANY +#include <X11/keysymdef.h> + +#include <X11/Xlib.h> +#include <X11/extensions/Xcomposite.h> +#include <X11/extensions/shape.h> + +#include <comp.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 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; +} + +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; + } + 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) +{ + struct timeval tv; + int diff; + + gettimeofday (&tv, 0); + + diff = TIMEVALDIFF (&tv, lastTv); + + 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 i, modMask[CompModNum]; + + for (i = 0; i < CompModNum; i++) + modMask[i] = 0; + + modmap = XGetModifierMapping (d->display); + if (modmap && modmap->max_keypermod > 0) + { + static int maskTable[] = { + ShiftMask, LockMask, ControlMask, Mod1Mask, + Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask + }; + KeySym keysym; + int i, size, mask; + + size = (sizeof (maskTable) / sizeof (int)) * modmap->max_keypermod; + + for (i = 0; i < size; ++i) + { + if (!modmap->modifiermap[i]) + continue; + + keysym = XKeycodeToKeysym (d->display, modmap->modifiermap[i], 0); + 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)); + + 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; + CompTimeout *t; + + 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 */ + 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) + { + XRaiseWindow (display->display, move->id); + continue; + } + } + 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) + { + XMoveWindow (display->display, move->id, + move->attrib.x + event.xbutton.x - px, + move->attrib.y + event.xbutton.y - py); + + px = event.xbutton.x; + py = event.xbutton.y; + 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; + } + + (*display->handleEvent) (display, &event); + } + + if (s->allDamaged || REGION_NOT_EMPTY (s->damage)) + { + if (timeToNextRedraw == 0) + { + /* wait for X drawing requests to finish + glXWaitX (); */ + + gettimeofday (&tv, 0); + + timeDiff = TIMEVALDIFF (&tv, &s->lastRedraw); + + (*s->preparePaintScreen) (s, timeDiff); + + if (s->allDamaged) + { + EMPTY_REGION (s->damage); + s->allDamaged = 0; + + (*s->paintScreen) (s, + &defaultScreenPaintAttrib, + &defaultWindowPaintAttrib, + &s->region, + PAINT_SCREEN_REGION_MASK | + PAINT_SCREEN_FULL_MASK); + + glXSwapBuffers (s->display->display, s->root); + } + else + { + XIntersectRegion (s->damage, &s->region, tmpRegion); + + EMPTY_REGION (s->damage); + + if ((*s->paintScreen) (s, + &defaultScreenPaintAttrib, + &defaultWindowPaintAttrib, + tmpRegion, + PAINT_SCREEN_REGION_MASK)) + { + BoxPtr pBox; + int nBox, y; + + 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, + &defaultWindowPaintAttrib, + &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--; + } + } + + timeToNextRedraw = getTimeToNextRedraw (s, &s->lastRedraw); + if (timeToNextRedraw) + timeToNextRedraw = poll (&ufd, 1, timeToNextRedraw); + } + 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); + } + } + + s->lastRedraw = lastTimeout = tv; + } + else + { + poll (&ufd, 1, 1000); + gettimeofday (&s->lastRedraw, 0); + } + + /* just redraw immediately */ + timeToNextRedraw = 0; + } + } +} + +static int errors = 0; +static int redirectFailed; + +static int +errorHandler (Display *dpy, + XErrorEvent *e) +{ + +#ifdef DEBUG + char str[128]; + char *name = 0; + int o; +#endif + + errors++; + + if (e->request_code == compDisplays->compositeOpcode && + e->minor_code == X_CompositeRedirectSubwindows) + { + redirectFailed = 1; + return 0; + } + +#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 (void) +{ + int e; + + e = errors; + errors = 0; + + return e; +} + +Bool +addDisplay (char *name, + char **plugin, + int nPlugin) +{ + CompDisplay *d; + Display *dpy; + int 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->plugin.list.type = CompOptionTypeString; + d->plugin.list.nValue = 0; + d->plugin.list.value = 0; + + compDisplayInitOptions (d, plugin, nPlugin); + + d->textureFilter = GL_LINEAR; + + d->display = dpy = XOpenDisplay (name); + if (!d->display) + { + fprintf (stderr, "%s: Couldn't open display %s\n", + programName, XDisplayName (name)); + return FALSE; + } + +#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->handleDamageEvent = handleDamageEvent; + + d->winTypeAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE", 0); + d->winDesktopAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_DESKTOP", 0); + d->winDockAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_DOCK", 0); + d->winToolbarAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_TOOLBAR", 0); + d->winMenuAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_MENU", 0); + d->winUtilAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_UTILITY", 0); + d->winSplashAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_SPLASH", 0); + d->winDialogAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_DIALOG", 0); + d->winNormalAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_NORMAL", 0); + d->winOpacityAtom = XInternAtom (dpy, "_NET_WM_WINDOW_OPACITY", 0); + d->winActiveAtom = XInternAtom (dpy, "_NET_ACTIVE_WINDOW", 0); + + d->wmStateAtom = XInternAtom (dpy, "WM_STATE", 0); + d->wmDeleteWindowAtom = XInternAtom (dpy, "WM_DELETE_WINDOW", 0); + + d->xBackgroundAtom[0] = XInternAtom (dpy, "_XSETROOT_ID", 0); + d->xBackgroundAtom[1] = XInternAtom (dpy, "_XROOTPMAP_ID", 0); + + 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; + } + } + + d->shapeExtension = XShapeQueryExtension (dpy, + &d->shapeEvent, + &d->shapeError); + + compDisplays = d; + + if (testMode) + { + addScreen (d, 0); + } + else + { + XGrabServer (dpy); + + for (i = 0; i < ScreenCount (dpy); i++) + { + redirectFailed = 0; + XCompositeRedirectSubwindows (dpy, XRootWindow (dpy, i), + CompositeRedirectManual); + XSync (dpy, FALSE); + if (redirectFailed) + { + fprintf (stderr, "%s: Another composite manager is already " + "running on screen: %d\n", programName, i); + } + else + { + if (!addScreen (d, i)) + { + fprintf (stderr, "%s: Failed to manage screen: %d\n", + programName, i); + } + } + } + + XUngrabServer (dpy); + } + + if (!d->screens) + { + fprintf (stderr, "%s: No managable screens found on display %s\n", + programName, XDisplayName (name)); + return FALSE; + } + + return TRUE; +} + +CompScreen * +findScreenAtDisplay (CompDisplay *d, + Window root) +{ + CompScreen *s; + + for (s = d->screens; s; s = s->next) + { + if (s->root == root) + return s; + } + + return 0; +} + +CompWindow * +findWindowAtDisplay (CompDisplay *d, + Window id) +{ + CompScreen *s; + CompWindow *w; + + for (s = d->screens; s; s = s->next) + { + w = findWindowAtScreen (s, id); + if (w) + return w; + } + + return 0; +} diff --git a/src/event.c b/src/event.c new file mode 100644 index 0000000..ccabfca --- /dev/null +++ b/src/event.c @@ -0,0 +1,268 @@ +/* + * 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 <X11/Xlib.h> +#include <X11/extensions/shape.h> + +#include <comp.h> + +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 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, 0); + break; + case DestroyNotify: + w = findWindowAtDisplay (display, event->xdestroywindow.window); + if (w) + { + addWindowDamage (w); + removeWindow (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); + 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) + { + addWindowDamage (w); + removeWindow (w); + } + } + break; + case CirculateNotify: + w = findWindowAtDisplay (display, event->xcirculate.window); + if (w) + circulateWindow (w, &event->xcirculate); + break; + case ButtonPress: + case ButtonRelease: + case KeyPress: + case KeyRelease: + break; + case PropertyNotify: + if (event->xproperty.atom == display->winActiveAtom) + { + s = findScreenAtDisplay (display, event->xproperty.window); + if (s) + s->activeWindow = getActiveWindow (display, s->root); + } + else if (event->xproperty.atom == display->winTypeAtom) + { + w = findWindowAtDisplay (display, event->xproperty.window); + if (w) + { + Atom type; + + type = getWindowType (display, w->client); + if (type != w->type) + { + + if (w->attrib.map_state == IsViewable) + { + if (w->type == display->winDesktopAtom) + w->screen->desktopWindowCount--; + else if (type == display->winDesktopAtom) + w->screen->desktopWindowCount++; + + addWindowDamage (w); + } + w->type = type; + } + } + } + else if (event->xproperty.atom == display->winOpacityAtom) + { + w = findWindowAtDisplay (display, event->xproperty.window); + if (w) + { + GLuint opacity; + + opacity = getWindowOpacity (display, w->id); + if (opacity != w->opacity) + { + w->opacity = opacity; + if (w->attrib.map_state == IsViewable) + 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); + } + } + break; + case MotionNotify: + break; + case ClientMessage: + if (event->xclient.format == 32 && + event->xclient.data.l[0] == display->wmDeleteWindowAtom) + exit (0); + break; + case MappingNotify: + updateModifierMappings (display); + break; + default: + if (display->shapeExtension && + event->type == display->shapeEvent + ShapeNotify) + { + w = findWindowAtDisplay (display, ((XShapeEvent *) event)->window); + if (w) + updateWindowRegion (w); + } + else if (event->type == display->damageEvent + XDamageNotify) + { + (*display->handleDamageEvent) (display, + (XDamageNotifyEvent *) event); + } + break; + } +} + +void +handleDamageEvent (CompDisplay *display, + XDamageNotifyEvent *event) +{ + CompScreen *screen; + REGION rect; + + rect.rects = &rect.extents; + rect.numRects = rect.size = 1; + + screen = display->screens; + if (screen->next) + { + CompWindow *w; + + w = findWindowAtDisplay (display, event->drawable); + if (!w) + return; + + screen = w->screen; + } + + rect.extents.x1 = event->geometry.x + event->area.x; + rect.extents.y1 = event->geometry.y + event->area.y; + rect.extents.x2 = rect.extents.x1 + event->area.width; + rect.extents.y2 = rect.extents.y1 + event->area.height; + + damageScreenRegion (screen, &rect); +} diff --git a/src/glxcompmgr.c b/src/glxcompmgr.c new file mode 100644 index 0000000..21673f3 --- /dev/null +++ b/src/glxcompmgr.c @@ -0,0 +1,146 @@ +/* + * 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 <comp.h> + +char *programName; +char **programArgv; +int programArgc; + +char *backgroundImage = "background.png"; +char *windowImage = "window.png"; + +REGION emptyRegion; +GLushort defaultColor[4] = { 0, 0, 0, 0 }; +Window currentRoot = 0; + +int defaultRefreshRate = 60; +char *defaultTextureFilter = "Good"; + +Bool testMode = FALSE; +Bool restartSignal = FALSE; + +static void +usage (void) +{ + printf ("Usage: %s " + "[--display DISPLAY] " + "[--bg-image PNG] " + "[--window-image PNG]\n " + "[--refresh-rate RATE] " + "[--fast-filter] " + "[--test-mode]\n " + "[--help] " + "[PLUGIN]...\n", + programName); +} + +static void +signalHandler (int sig) +{ + if (sig == SIGHUP) + restartSignal = TRUE; +} + +int +main (int argc, char **argv) +{ + char *displayName = 0; + char *plugin[256]; + int i, nPlugin = 0; + + programName = argv[0]; + programArgc = argc; + programArgv = argv; + + signal (SIGHUP, 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; + + 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], "--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 0000000..7369b5e --- /dev/null +++ b/src/option.c @@ -0,0 +1,256 @@ +/* + * 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 <comp.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) +{ + if (option->rest.s.nString) + { + int i; + + if (!value->s) + return FALSE; + + for (i = 0; i < option->rest.s.nString; i++) + { + if (strcmp (option->rest.s.string[i], value->s) == 0) + break; + } + + if (i == option->rest.s.nString) + return FALSE; + } + + if (option->value.s == value->s) + return FALSE; + + if (option->value.s && value->s) + { + if (strcmp (option->value.s, value->s) == 0) + return FALSE; + } + + if (option->value.s) + free (option->value.s); + + if (value->s) + option->value.s = strdup (value->s); + else + option->value.s = 0; + + 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; +} diff --git a/src/paint.c b/src/paint.c new file mode 100644 index 0000000..5174e95 --- /dev/null +++ b/src/paint.c @@ -0,0 +1,555 @@ +/* + * 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 <comp.h> + +ScreenPaintAttrib defaultScreenPaintAttrib = { + 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f +}; + +WindowPaintAttrib defaultWindowPaintAttrib = { + OPAQUE, 0.0f, 0.0f, 1.0f, 1.0f +}; + +void +preparePaintScreen (CompScreen *screen, + int msSinceLastPaint) {} + +void +donePaintScreen (CompScreen *screen) {} + +void +paintTransformedScreen (CompScreen *screen, + const ScreenPaintAttrib *sAttrib, + const WindowPaintAttrib *wAttrib, + unsigned int mask) +{ + CompWindow *w; + int windowMask; + int backgroundMask; + + glPushMatrix (); + + glTranslatef (sAttrib->xTranslate, + sAttrib->yTranslate, + sAttrib->zTranslate - BASE_Z_TRANSLATE); + + glRotatef (sAttrib->xRotate, 0.0f, 1.0f, 0.0f); + glRotatef (sAttrib->vRotate, + 1.0f - sAttrib->xRotate / 90.0f, + 0.0f, + sAttrib->xRotate / 90.0f); + glRotatef (sAttrib->yRotate, 0.0f, 1.0f, 0.0f); + + glTranslatef (-0.5f, -0.5f, 0.5f); + 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) + (*screen->paintWindow) (w, wAttrib, &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) + (*screen->paintWindow) (w, wAttrib, &screen->region, windowMask); + + glPopMatrix (); +} + +Bool +paintScreen (CompScreen *screen, + const ScreenPaintAttrib *sAttrib, + const WindowPaintAttrib *wAttrib, + Region region, + unsigned int mask) +{ + static Region tmpRegion = NULL; + CompWindow *w; + + if (mask & PAINT_SCREEN_REGION_MASK) + { + if ((mask & PAINT_SCREEN_TRANSFORMED_MASK) || + (mask & PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS_MASK)) + { + if (mask & PAINT_SCREEN_FULL_MASK) + { + (*screen->paintTransformedScreen) (screen, sAttrib, wAttrib, + mask); + + return TRUE; + } + + return FALSE; + } + + /* fall through and redraw region */ + } + else if (mask & PAINT_SCREEN_FULL_MASK) + { + (*screen->paintTransformedScreen) (screen, sAttrib, wAttrib, mask); + + return TRUE; + } + else + return FALSE; + + if (!tmpRegion) + { + tmpRegion = XCreateRegion (); + if (!tmpRegion) + return FALSE; + } + + XSubtractRegion (region, &emptyRegion, tmpRegion); + + glPushMatrix (); + + glTranslatef (0.0f, 0.0f, -BASE_Z_TRANSLATE); + + glTranslatef (-0.5f, -0.5f, 0.5f); + glScalef (1.0f / screen->width, -1.0f / screen->height, 1.0f); + glTranslatef (0.0f, -screen->height, 0.0f); + + /* paint solid windows */ + for (w = screen->reverseWindows; w; w = w->prev) + { + if (w->invisible) + continue; + + if ((*screen->paintWindow) (w, wAttrib, tmpRegion, + PAINT_WINDOW_SOLID_MASK)) + XSubtractRegion (tmpRegion, w->region, tmpRegion); + + /* copy region */ + XSubtractRegion (tmpRegion, &emptyRegion, w->clip); + } + + (*screen->paintBackground) (screen, tmpRegion, 0); + + /* paint translucent windows */ + for (w = screen->windows; w; w = w->next) + { + if (w->invisible) + continue; + + (*screen->paintWindow) (w, wAttrib, w->clip, + PAINT_WINDOW_TRANSLUCENT_MASK); + } + + glPopMatrix (); + + return TRUE; +} + +#define ADD_QUAD(data, w, x1, y1, x2, y2) \ + if (!(w)->pixmap) \ + bindWindow (w); \ + *(data)++ = X_WINDOW_TO_TEXTURE_SPACE (w, x1); \ + *(data)++ = Y_WINDOW_TO_TEXTURE_SPACE (w, y2); \ + *(data)++ = (x1); \ + *(data)++ = (y2); \ + *(data)++ = X_WINDOW_TO_TEXTURE_SPACE (w, x2); \ + *(data)++ = Y_WINDOW_TO_TEXTURE_SPACE (w, y2); \ + *(data)++ = (x2); \ + *(data)++ = (y2); \ + *(data)++ = X_WINDOW_TO_TEXTURE_SPACE (w, x2); \ + *(data)++ = Y_WINDOW_TO_TEXTURE_SPACE (w, y1); \ + *(data)++ = (x2); \ + *(data)++ = (y1); \ + *(data)++ = X_WINDOW_TO_TEXTURE_SPACE (w, x1); \ + *(data)++ = Y_WINDOW_TO_TEXTURE_SPACE (w, y1); \ + *(data)++ = (x1); \ + *(data)++ = (y1) + +#define ADD_BOX(data, w, box) \ + ADD_QUAD (data, w, (box)->x1, (box)->y1, (box)->x2, (box)->y2) + +Bool +paintWindow (CompWindow *w, + const WindowPaintAttrib *attrib, + Region region, + unsigned int mask) +{ + BoxPtr pClip; + int nClip, n; + GLfloat *data, *d; + GLushort opacity; + int x1, y1, x2, y2; + + if (!region->numRects) + return TRUE; + + if (w->destroyed || w->attrib.map_state != IsViewable) + return TRUE; + + if (mask & PAINT_WINDOW_SOLID_MASK) + { + if (w->alpha) + return FALSE; + + opacity = MULTIPLY_USHORT (w->opacity, attrib->opacity); + if (opacity != OPAQUE) + return FALSE; + } + else if (mask & PAINT_WINDOW_TRANSLUCENT_MASK) + { + opacity = MULTIPLY_USHORT (w->opacity, attrib->opacity); + if (!w->alpha && opacity == OPAQUE) + return FALSE; + } + else + { + opacity = MULTIPLY_USHORT (w->opacity, attrib->opacity); + if (w->alpha || opacity != OPAQUE) + mask |= PAINT_WINDOW_TRANSLUCENT_MASK; + else + mask |= PAINT_WINDOW_SOLID_MASK; + } + + if (attrib->xTranslate != 0.0f || + attrib->yTranslate != 0.0f || + attrib->xScale != 1.0f || + attrib->yScale != 1.0f) + { + nClip = w->region->numRects; + pClip = w->region->rects; + + mask |= PAINT_WINDOW_ON_TRANSFORMED_SCREEN_MASK; + + data = malloc (sizeof (GLfloat) * nClip * 16); + if (!data) + return FALSE; + + d = data; + + n = nClip; + while (nClip--) + { + x1 = pClip->x1 - w->attrib.x; + y1 = pClip->y1 - w->attrib.y; + x2 = pClip->x2 - w->attrib.x; + y2 = pClip->y2 - w->attrib.y; + + ADD_QUAD (d, w, x1, y1, x2, y2); + + pClip++; + } + } + else + { + BoxRec clip, full; + BoxPtr pExtent = ®ion->extents; + BoxPtr pBox = region->rects; + int nBox = region->numRects; + int dataSize; + + full.x1 = 0; + full.y1 = 0; + full.x2 = w->width; + full.y2 = w->height; + + x1 = pExtent->x1 - w->attrib.x; + y1 = pExtent->y1 - w->attrib.y; + x2 = pExtent->x2 - w->attrib.x; + y2 = pExtent->y2 - w->attrib.y; + + if (x1 > 0) + full.x1 = x1; + if (y1 > 0) + full.y1 = y1; + if (x2 < w->width) + full.x2 = x2; + if (y2 < w->height) + full.y2 = y2; + + if (full.x1 >= full.x2 || full.y1 >= full.y2) + return TRUE; + + dataSize = nBox * 16; + data = malloc (sizeof (GLfloat) * dataSize); + if (!data) + return FALSE; + + d = data; + n = 0; + + pBox = region->rects; + nBox = region->numRects; + while (nBox--) + { + x1 = pBox->x1 - w->attrib.x; + y1 = pBox->y1 - w->attrib.y; + x2 = pBox->x2 - w->attrib.x; + y2 = pBox->y2 - w->attrib.y; + + 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 = w->region->numRects; + + if (nClip == 1) + { + ADD_QUAD (d, w, x1, y1, x2, y2); + + n++; + } + else + { + pClip = w->region->rects; + + while (nClip--) + { + clip.x1 = pClip->x1 - w->attrib.x; + clip.y1 = pClip->y1 - w->attrib.y; + clip.x2 = pClip->x2 - w->attrib.x; + clip.y2 = pClip->y2 - w->attrib.y; + + pClip++; + + if (clip.x1 < x1) + clip.x1 = x1; + if (clip.y1 < y1) + clip.y1 = y1; + if (clip.x2 > x2) + clip.x2 = x2; + if (clip.y2 > y2) + clip.y2 = y2; + + if (clip.x1 < clip.x2 && clip.y1 < clip.y2) + { + if ((n << 4) == dataSize) + { + dataSize <<= 2; + data = realloc (data, + sizeof (GLfloat) * dataSize); + if (!data) + return FALSE; + + d = data + (n * 16); + } + + ADD_BOX (d, w, &clip); + + n++; + } + } + } + } + } + } + + if (n) + { + glTexCoordPointer (2, GL_FLOAT, sizeof (GLfloat) * 4, data); + glVertexPointer (2, GL_FLOAT, sizeof (GLfloat) * 4, data + 2); + + if (mask & PAINT_WINDOW_TRANSLUCENT_MASK) + { + glEnable (GL_BLEND); + if (opacity != OPAQUE) + { + glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glColor4us (opacity, opacity, opacity, opacity); + } + } + + glPushMatrix (); + + if (mask & PAINT_WINDOW_ON_TRANSFORMED_SCREEN_MASK) + { + glTranslatef (w->attrib.x + attrib->xTranslate, + w->attrib.y + attrib->yTranslate, 0.0f); + glScalef (attrib->xScale, attrib->yScale, 0.0f); + + enableTexture (w->screen, &w->texture, COMP_TEXTURE_FILTER_GOOD); + } + else + { + glTranslatef (w->attrib.x, w->attrib.y, 0.0f); + + enableTexture (w->screen, &w->texture, COMP_TEXTURE_FILTER_FAST); + } + + glDrawArrays (GL_QUADS, 0, n * 4); + + disableTexture (&w->texture); + + glPopMatrix (); + + if (mask & PAINT_WINDOW_TRANSLUCENT_MASK) + { + if (opacity != OPAQUE) + { + glColor4usv (defaultColor); + glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + } + glDisable (GL_BLEND); + } + } + + free (data); + + 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++ = bg->dx * pBox->x1; + *d++ = bg->dy * (s->backgroundHeight - pBox->y2); + + *d++ = pBox->x1; + *d++ = pBox->y2; + + *d++ = bg->dx * pBox->x2; + *d++ = bg->dy * (s->backgroundHeight - pBox->y2); + + *d++ = pBox->x2; + *d++ = pBox->y2; + + *d++ = bg->dx * pBox->x2; + *d++ = bg->dy * (s->backgroundHeight - pBox->y1); + + *d++ = pBox->x2; + *d++ = pBox->y1; + + *d++ = bg->dx * pBox->x1; + *d++ = bg->dy * (s->backgroundHeight - 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 0000000..b679070 --- /dev/null +++ b/src/plugin.c @@ -0,0 +1,358 @@ +/* + * 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 <comp.h> + +#define HOME_PLUGINDIR ".glxcomp/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; + + 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; + + p->dlhand = dlopen (file, RTLD_LAZY); + if (!p->dlhand) + { + char *home, *plugindir; + + 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) + { + PluginGetInfoProc getInfo; + + dlerror (); + + getInfo = (PluginGetInfoProc) dlsym (p->dlhand, "getCompPluginInfo"); + + if (dlerror () != 0) + 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); +} + +Bool +pushPlugin (CompPlugin *p) +{ + if (findActivePlugin (p->vTable->name)) + { + fprintf (stderr, "%s: Plugin '%s' already active\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 0000000..b004452 --- /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 <comp.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 0000000..a9e1661 --- /dev/null +++ b/src/readpng.c @@ -0,0 +1,199 @@ +/* + * 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 <comp.h> + +#define HOME_IMAGEDIR ".glxcomp/images" + +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)); + } +} + +Bool +readPng (const char *filename, + char **data, + unsigned int *width, + unsigned int *height) +{ + static const int PNG_SIG_SIZE = 8; + unsigned char png_sig[PNG_SIG_SIZE]; + FILE *file; + int sig_bytes; + png_struct *png; + png_info *info; + png_uint_32 png_width, png_height; + int depth, color_type, interlace, i; + unsigned int pixel_size; + png_byte **row_pointers; + + 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) + { + fclose (file); + return FALSE; + } + + info = png_create_info_struct (png); + if (info == NULL) + { + fclose (file); + png_destroy_read_struct (&png, NULL, NULL); + return FALSE; + } + + png_init_io (png, file); + png_set_sig_bytes (png, sig_bytes); + + 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) + { + fclose (file); + return FALSE; + } + + row_pointers = (png_byte **) malloc (png_height * sizeof (char *)); + 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); + fclose (file); + + png_destroy_read_struct (&png, &info, NULL); + + return TRUE; +} diff --git a/src/screen.c b/src/screen.c new file mode 100644 index 0000000..3664961 --- /dev/null +++ b/src/screen.c @@ -0,0 +1,1294 @@ +/* + * 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 <math.h> +#include <dlfcn.h> +#include <string.h> + +#include <comp.h> + +#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); +} + +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_REFRESH_RATE: + if (compSetIntOption (o, value)) + { + screen->redrawTime = 1000 / o->value.i; + 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_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; +} + +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 +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; + + 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; +} + +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); + } + + screen->backgroundWidth = width; + screen->backgroundHeight = height; +} + +Bool +addScreen (CompDisplay *display, + int screenNum) +{ + CompScreen *s; + Display *dpy = display->display; + static char data = 0; + XColor black, dummy; + 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; + + 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; + + compScreenInitOptions (s); + + s->redrawTime = 1000 / s->opt[COMP_SCREEN_OPTION_REFRESH_RATE].value.i; + + s->display = display; + + s->damage = XCreateRegion (); + if (!s->damage) + return FALSE; + + 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->screenNum = screenNum; + s->colormap = DefaultColormap (dpy, screenNum); + s->root = XRootWindow (dpy, screenNum); + + if (testMode) + { + XSetWindowAttributes attrib; + 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 = "glxcompmgr"; + classHint->res_class = "Glxcompmgr"; + + 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); + + XSetWMProtocols (dpy, s->root, &display->wmDeleteWindowAtom, 1); + + XmbSetWMProperties (dpy, s->root, + "glxcompmgr - Test mode", "glxcompmgr", + programArgv, programArgc, + normalHints, wmHints, classHint); + + s->fake[0] = XCreateWindow (dpy, s->root, 64, 32, 1, 1, 0, + visinfo->depth, InputOutput, + visinfo->visual, + CWColormap, &attrib); + + s->fake[1] = XCreateWindow (dpy, s->root, 256, 256, 1, 1, 0, + visinfo->depth, InputOutput, + visinfo->visual, + CWColormap, &attrib); + + XMapWindow (dpy, s->root); + + XFree (wmHints); + XFree (classHint); + XFree (normalHints); + } + else + s->fake[0] = s->fake[1] = 0; + + s->escapeKeyCode = XKeysymToKeycode (display->display, + XStringToKeysym ("Escape")); + + s->allDamaged = TRUE; + 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; + + 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->invisibleWindowMove = invisibleWindowMove; + + s->getProcAddress = 0; + + if (s->root) + { + XSetWindowAttributes attrib; + + attrib.override_redirect = 1; + s->grabWindow = XCreateWindow (dpy, s->root, -100, -100, 1, 1, 0, + CopyFromParent, CopyFromParent, + CopyFromParent, CWOverrideRedirect, + &attrib); + + XMapWindow (dpy, s->grabWindow); + } + + if (!XGetWindowAttributes (dpy, s->root, &s->attrib)) + return FALSE; + + s->activeWindow = 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; + + if (!XAllocNamedColor (dpy, s->colormap, "black", &black, &dummy)) + { + 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; + } + + 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_MESA_render_texture")) + { + fprintf (stderr, "%s: GLX_MESA_render_texture is missing\n", + programName); + return FALSE; + } + + s->getProcAddress = (GLXGetProcAddressProc) + getProcAddress (s, "glXGetProcAddressARB"); + s->bindTexImage = (GLXBindTexImageProc) + getProcAddress (s, "glXBindTexImageMESA"); + s->releaseTexImage = (GLXReleaseTexImageProc) + getProcAddress (s, "glXReleaseTexImageMESA"); + s->queryDrawable = (GLXQueryDrawableProc) + getProcAddress (s, "glXQueryDrawable"); + + if (!testMode && !s->bindTexImage) + { + fprintf (stderr, "%s: glXBindTexImageMESA is missing\n", programName); + return FALSE; + } + + if (!testMode && !s->releaseTexImage) + { + fprintf (stderr, "%s: glXReleaseTexImageMESA is missing\n", + programName); + return FALSE; + } + + if (!testMode && !s->queryDrawable) + { + fprintf (stderr, "%s: glXQueryDrawable is missing\n", programName); + return FALSE; + } + + 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; + + 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; + } + + 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); + glEnableClientState (GL_VERTEX_ARRAY); + glEnableClientState (GL_TEXTURE_COORD_ARRAY); + glColor4usv (defaultColor); + + s->activeWindow = getActiveWindow (display, s->root); + + reshape (s, s->attrib.width, s->attrib.height); + + s->next = display->screens; + display->screens = s; + + screenInitPlugins (s); + + XSelectInput (dpy, s->root, + SubstructureNotifyMask | + StructureNotifyMask | + PropertyChangeMask | + ExposureMask | + ButtonPressMask | + ButtonReleaseMask | + ButtonMotionMask); + + XQueryTree (dpy, s->root, + &rootReturn, &parentReturn, + &children, &nchildren); + + for (i = 0; i < nchildren; i++) + { + if (children[i] == s->grabWindow) + continue; + + addWindow (s, children[i], i ? children[i - 1] : 0); + } + + XFree (children); + + return TRUE; +} + +void +damageScreenRegion (CompScreen *screen, + Region region) +{ + if (screen->allDamaged) + return; + + XUnionRegion (screen->damage, region, screen->damage); +} + +void +damageScreen (CompScreen *s) +{ + s->allDamaged = TRUE; +} + +CompWindow * +findWindowAtScreen (CompScreen *s, + Window id) +{ + CompWindow *w; + + for (w = s->windows; w; w = w->next) + if (w->id == id) + return w; + + return 0; +} + +CompWindow * +findClientWindowAtScreen (CompScreen *s, + Window id) +{ + CompWindow *w; + + for (w = s->windows; w; w = w->next) + if (w->client == id) + return w; + + return 0; +} + +void +insertWindowIntoScreen (CompScreen *s, + CompWindow *w, + Window aboveId) +{ + CompWindow *p; + + if (s->windows) + { + for (p = s->windows; p; p = p->next) + { + if (p->id == aboveId) + { + w->next = p->next; + w->prev = p; + if (p->next) + p->next->prev = w; + p->next = w; + + if (s->reverseWindows == p) + s->reverseWindows = w; + + return; + } + + if (!p->next) + { + p->next = w; + w->next = NULL; + w->prev = p; + + s->reverseWindows = w; + + return; + } + } + } + else + { + s->reverseWindows = s->windows = w; + w->prev = w->next = NULL; + } +} + +void +unhookWindowFromScreen (CompScreen *s, + CompWindow *w) +{ + CompWindow *p; + + if (s->windows == w) + { + s->windows = w->next; + if (w->next) + w->next->prev = NULL; + } + + if (s->reverseWindows == w) + { + s->reverseWindows = w->prev; + if (w->prev) + w->prev->next = NULL; + } + + for (p = s->windows; p; p = p->next) + { + if (p->next == w) + { + p->next = w->next; + if (w->next) + w->next->prev = p; + + p->next = w->next; + break; + } + } +} + +#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; + 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 (); + + XGrabKey (s->display->display, + key->keycode, + mask, + s->root, + TRUE, + GrabModeAsync, + GrabModeAsync); + + XSync (s->display->display, FALSE); + + if (compCheckForError ()) + { + +#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; + 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)) + { + XUngrabKey (s->display->display, + key->keycode, + mask, + s->root); + } + } + } +} + +static void +updatePassiveKeyGrabs (CompScreen *s) +{ + unsigned int mask; + 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)) + { + XGrabKey (s->display->display, + s->keyGrab[i].keycode, + mask, + s->root, + TRUE, + GrabModeAsync, + GrabModeAsync); + } + } +} + +static Bool +addPassiveButtonGrab (CompScreen *s, + CompButtonBinding *button) +{ + CompButtonGrab *buttonGrab; + unsigned int modifiers, mask; + 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; + + mask = virtualToRealModMask (s->display, modifiers); + if (!(mask & CompNoMask)) + { + compCheckForError (); + + XGrabButton (s->display->display, + button->button, + mask, + s->root, + TRUE, + POINTER_GRAB_MASK, + GrabModeAsync, + GrabModeAsync, + None, + None); + + XSync (s->display->display, FALSE); + + if (compCheckForError ()) + { + +#ifdef DEBUG + fprintf (stderr, "XGrabButton failed: %s 0x%x\n", + button->button, modifiers); +#endif + + return FALSE; + } + } + + 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, mask; + 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); + + mask = virtualToRealModMask (s->display, modifiers); + if (!(mask & CompNoMask)) + { + XUngrabButton (s->display->display, + button->button, + mask, + s->root); + } + } + } +} + +static void +updatePassiveButtonGrabs (CompScreen *s) +{ + unsigned int mask; + int i; + + XUngrabButton (s->display->display, AnyButton, AnyModifier, s->root); + + for (i = 0; i < s->nButtonGrab; i++) + { + mask = virtualToRealModMask (s->display, s->buttonGrab[i].modifiers); + if (!(mask & CompNoMask)) + { + XGrabButton (s->display->display, + s->buttonGrab[i].button, + mask, + s->root, + TRUE, + POINTER_GRAB_MASK, + GrabModeAsync, + GrabModeAsync, + None, + None); + } + } +} + +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) +{ + updatePassiveButtonGrabs (s); + updatePassiveKeyGrabs (s); +} diff --git a/src/texture.c b/src/texture.c new file mode 100644 index 0000000..6ef4e74 --- /dev/null +++ b/src/texture.c @@ -0,0 +1,286 @@ +/* + * 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 <comp.h> + +void +initTexture (CompScreen *screen, + CompTexture *texture) +{ + texture->name = 0; + texture->target = GL_TEXTURE_2D; + texture->dx = 0.0f; + texture->dy = 0.0f; + texture->pixmap = None; + texture->filter = COMP_TEXTURE_FILTER_FAST; +} + +void +finiTexture (CompScreen *screen, + CompTexture *texture) +{ + if (texture->name) + { + releasePixmapFromTexture (screen, texture); + glDeleteTextures (1, &texture->name); + } +} + +Bool +readImageToTexture (CompScreen *screen, + CompTexture *texture, + char *imageFileName, + unsigned int *returnWidth, + unsigned int *returnHeight) +{ + char *data, *image; + unsigned int width, height; + int i; + + if (!readPng (imageFileName, &image, &width, &height)) + { + fprintf (stderr, "%s: Failed to load image: %s\n", + programName, imageFileName); + return FALSE; + } + + data = malloc (4 * width * height); + if (!data) + { + free (image); + return FALSE; + } + + for (i = 0; i < height; i++) + memcpy (&data[i * width * 4], + &image[(height - i - 1) * width * 4], + width * 4); + + free (image); + + releasePixmapFromTexture (screen, texture); + + if (screen->textureNonPowerOfTwo || + (POWER_OF_TWO (width) && POWER_OF_TWO (height))) + { + texture->target = GL_TEXTURE_2D; + texture->dx = 1.0f / width; + texture->dy = 1.0f / height; + } + else + { + texture->target = GL_TEXTURE_RECTANGLE_NV; + texture->dx = texture->dy = 1.0f; + } + + if (!texture->name) + glGenTextures (1, &texture->name); + + glBindTexture (texture->target, texture->name); + + glTexImage2D (texture->target, 0, GL_RGB, 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); + + glBindTexture (texture->target, 0); + + free (data); + + *returnWidth = width; + *returnHeight = height; + + return TRUE; +} + +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; + } + + if (screen->queryDrawable (screen->display->display, + texture->pixmap, + GLX_TEXTURE_TARGET_EXT, + &target)) + { + fprintf (stderr, "%s: glXQueryDrawable failed\n", programName); + + glXDestroyGLXPixmap (screen->display->display, texture->pixmap); + texture->pixmap = None; + + return FALSE; + } + + switch (target) { + case GLX_TEXTURE_2D_EXT: + texture->target = GL_TEXTURE_2D; + texture->dx = 1.0f / width; + texture->dy = 1.0f / height; + break; + case GLX_TEXTURE_RECTANGLE_EXT: + texture->target = GL_TEXTURE_RECTANGLE_ARB; + texture->dx = texture->dy = 1.0f; + break; + default: + fprintf (stderr, "%s: pixmap 0x%x can't be bound to texture\n", + programName, (int) pixmap); + + 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); + + glBindTexture (texture->target, 0); + + return TRUE; +} + +void +releasePixmapFromTexture (CompScreen *screen, + CompTexture *texture) +{ + if (texture->pixmap) + { + 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 0000000..8c9a126 --- /dev/null +++ b/src/window.c @@ -0,0 +1,605 @@ +/* + * 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/extensions/shape.h> +#include <X11/extensions/Xcomposite.h> + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include <comp.h> + +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 Window +tryChildren (CompDisplay *display, + Window win) +{ + Window root, parent; + Window *children; + unsigned int nchildren; + unsigned int i; + Atom type = None; + int format; + unsigned long nitems, after; + unsigned char *data; + Window inf = 0; + + if (!XQueryTree (display->display, win, &root, &parent, + &children, &nchildren)) + return 0; + + for (i = 0; !inf && (i < nchildren); i++) + { + data = NULL; + + XGetWindowProperty (display->display, children[i], + display->wmStateAtom, 0, 0, + False, AnyPropertyType, &type, &format, &nitems, + &after, &data); + if (data) + XFree (data); + + if (type) + inf = children[i]; + } + + for (i = 0; !inf && (i < nchildren); i++) + inf = tryChildren (display, children[i]); + + if (children) + XFree (children); + + return inf; +} + +static Window +clientWindow (CompDisplay *display, + Window win) +{ + Atom type = None; + int format; + unsigned long nitems, after; + unsigned char *data = NULL; + Window inf; + + XGetWindowProperty (display->display, win, display->wmStateAtom, 0, 0, + False, AnyPropertyType, &type, &format, &nitems, + &after, &data); + + if (data) + XFree (data); + + if (type) + return win; + + inf = tryChildren (display, win); + if (!inf) + inf = win; + + return inf; +} + +Window +getActiveWindow (CompDisplay *display, + Window root) +{ + Atom actual; + int result, format; + unsigned long n, left; + unsigned char *data; + + result = XGetWindowProperty (display->display, root, + display->winActiveAtom, 0L, 1L, False, + XA_WINDOW, &actual, &format, + &n, &left, &data); + + if (result == Success && n && data) + { + Window w; + + memcpy (&w, data, sizeof (Window)); + XFree ((void *) data); + + return w; + } + + return None; +} + +Atom +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); + + return a; + } + + return display->winNormalAtom; +} + +unsigned short +getWindowOpacity (CompDisplay *display, + Window id) +{ + Atom actual; + int result, format; + unsigned long n, left; + unsigned char *data; + + result = XGetWindowProperty (display->display, id, display->winOpacityAtom, + 0L, 1L, FALSE, XA_CARDINAL, &actual, &format, + &n, &left, &data); + + if (result == Success && n && data) + { + unsigned int o; + + memcpy (&o, data, sizeof (Atom)); + XFree ((void *) data); + + return (o / 0xffff); + } + + return MAXSHORT; +} + +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; + } + else + { + w->pixmap = XCompositeNameWindowPixmap (w->screen->display->display, + w->id); + if (!w->pixmap) + { + fprintf (stderr, "%s: XCompositeNameWindowPixmap failed\n", + programName); + return; + } + + 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); + } + } +} + +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->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); + + free (w); +} + +void +addWindowDamage (CompWindow *w) +{ + if (w->attrib.map_state == IsViewable) + damageScreenRegion (w->screen, w->region); +} + +void +updateWindowRegion (CompWindow *w) +{ + REGION rect; + XRectangle r, *rects, *shapeRects = 0; + int i, n = 0; + + EMPTY_REGION (w->region); + + if (w->screen->display->shapeExtension) + { + int order; + + shapeRects = XShapeGetRectangles (w->screen->display->display, w->id, + ShapeBounding, &n, &order); + } + + if (n < 2) + { + r.x = 0; + r.y = 0; + 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.x; + rect.extents.y1 = rects[i].y + w->attrib.y; + rect.extents.x2 = rect.extents.x1 + rects[i].width; + rect.extents.y2 = rect.extents.y1 + rects[i].height; + + XUnionRegion (&rect, w->region, w->region); + } + + if (shapeRects) + XFree (shapeRects); +} + +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->screen = screen; + w->texture.name = 0; + w->pixmap = None; + w->destroyed = FALSE; + + 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; + } + + XSelectInput (screen->display->display, id, PropertyChangeMask); + + w->id = id; + w->client = clientWindow (screen->display, id); + w->alpha = (w->attrib.depth == 32); + w->opacity = OPAQUE; + w->type = getWindowType (screen->display, w->client); + + w->width = w->attrib.width + w->attrib.border_width * 2; + w->height = w->attrib.height + w->attrib.border_width * 2; + + if (screen->display->shapeExtension) + XShapeSelectInput (screen->display->display, id, ShapeNotifyMask); + + updateWindowRegion (w); + + insertWindowIntoScreen (screen, w, aboveId); + + if (w->attrib.class != InputOnly) + { + initTexture (screen, &w->texture); + + w->damage = XDamageCreate (screen->display->display, id, + XDamageReportRawRectangles); + } + else + { + w->damage = None; + w->attrib.map_state = IsUnmapped; + } + + if (testMode) + { + w->attrib.map_state = IsViewable; + bindWindow (w); + } + + w->invisible = WINDOW_INVISIBLE (w); + + if (w->type == w->screen->display->winDesktopAtom) + w->screen->desktopWindowCount++; + + windowInitPlugins (w); + + addWindowDamage (w); +} + +void +removeWindow (CompWindow *w) +{ + if (w->attrib.map_state == IsViewable) + { + if (w->type == w->screen->display->winDesktopAtom) + w->screen->desktopWindowCount++; + } + + unhookWindowFromScreen (w->screen, w); + windowFiniPlugins (w); + freeWindow (w); +} + +void +destroyWindow (CompWindow *w) +{ + if (!w->destroyed) + { + w->destroyed = TRUE; + w->screen->pendingDestroys++; + } +} + +void +mapWindow (CompWindow *w) +{ + if (w->attrib.class == InputOnly) + return; + + if (w->attrib.map_state == IsViewable) + return; + + if (w->type == w->screen->display->winDesktopAtom) + w->screen->desktopWindowCount++; + + w->attrib.map_state = IsViewable; + w->invisible = WINDOW_INVISIBLE (w); + + addWindowDamage (w); +} + +void +unmapWindow (CompWindow *w) +{ + if (w->attrib.map_state != IsViewable) + return; + + if (w->type == w->screen->display->winDesktopAtom) + w->screen->desktopWindowCount--; + + addWindowDamage (w); + + w->attrib.map_state = IsUnmapped; + w->invisible = WINDOW_INVISIBLE (w); + + releaseWindow (w); +} + +static int +restackWindow (CompWindow *w, + Window aboveId) +{ + if (w->prev) + { + if (aboveId == w->prev->id) + return 0; + } + else if (aboveId == None) + return 0; + + unhookWindowFromScreen (w->screen, w); + insertWindowIntoScreen (w->screen, w, aboveId); + + return 1; +} + +void +configureWindow (CompWindow *w, + XConfigureEvent *ce) +{ + Bool damage; + + if (w->attrib.width != ce->width || + w->attrib.height != ce->height || + w->attrib.border_width != ce->border_width) + { + addWindowDamage (w); + + w->attrib.x = ce->x; + w->attrib.y = ce->y; + w->attrib.width = ce->width; + w->attrib.height = ce->height; + w->attrib.border_width = ce->border_width; + + w->width = w->attrib.width + w->attrib.border_width * 2; + w->height = w->attrib.height + w->attrib.border_width * 2; + + releaseWindow (w); + + EMPTY_REGION (w->region); + + damage = TRUE; + } + else if (w->attrib.x != ce->x || w->attrib.y != ce->y) + { + addWindowDamage (w); + + XOffsetRegion (w->region, ce->x - w->attrib.x, ce->y - w->attrib.y); + + w->attrib.x = ce->x; + w->attrib.y = ce->y; + + damage = TRUE; + } + else + damage = FALSE; + + w->attrib.override_redirect = ce->override_redirect; + + w->invisible = WINDOW_INVISIBLE (w); + + if (restackWindow (w, ce->above) || damage) + { + if (!REGION_NOT_EMPTY (w->region)) + updateWindowRegion (w); + + addWindowDamage (w); + } +} + +void +circulateWindow (CompWindow *w, + XCirculateEvent *ce) +{ + Window newAboveId; + + if (ce->place == PlaceOnTop && w->screen->windows) + newAboveId = w->screen->windows->id; + else + newAboveId = 0; + + if (restackWindow (w, newAboveId)) + addWindowDamage (w); +} + +void +invisibleWindowMove (CompWindow *w, + int dx, + int dy) +{ + w->attrib.x += dx; + w->attrib.y += dy; + + XOffsetRegion (w->region, dx, dy); +} |