diff options
author | David Reveman <c99drn@cs.umu.se> | 2006-02-09 06:03:09 +0000 |
---|---|---|
committer | David Reveman <c99drn@cs.umu.se> | 2006-02-09 06:03:09 +0000 |
commit | 9959c2b13ded64a5e66359a8097250dc9d87fc1c (patch) | |
tree | 23478d196cd4acb4a9d2949c0438e9df75c2e8d6 /src |
Initial revision
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 22 | ||||
-rw-r--r-- | src/action.c | 41 | ||||
-rw-r--r-- | src/display.c | 1844 | ||||
-rw-r--r-- | src/event.c | 1005 | ||||
-rw-r--r-- | src/main.c | 211 | ||||
-rw-r--r-- | src/option.c | 292 | ||||
-rw-r--r-- | src/paint.c | 864 | ||||
-rw-r--r-- | src/plugin.c | 404 | ||||
-rw-r--r-- | src/privates.c | 68 | ||||
-rw-r--r-- | src/readpng.c | 266 | ||||
-rw-r--r-- | src/screen.c | 2521 | ||||
-rw-r--r-- | src/texture.c | 337 | ||||
-rw-r--r-- | src/window.c | 2903 |
13 files changed, 10778 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 00000000..289e7e93 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,22 @@ +INCLUDES = \ + @COMPIZ_CFLAGS@ \ + -I$(top_srcdir)/include \ + -DPLUGINDIR=\"$(plugindir)\" \ + -DIMAGEDIR=\"$(imagedir)\" + +bin_PROGRAMS = compiz + +compiz_LDADD = @COMPIZ_LIBS@ @GL_LIBS@ -lm +compiz_LDFLAGS = -export-dynamic +compiz_SOURCES = \ + main.c \ + privates.c \ + texture.c \ + display.c \ + screen.c \ + window.c \ + event.c \ + paint.c \ + option.c \ + plugin.c \ + readpng.c diff --git a/src/action.c b/src/action.c new file mode 100644 index 00000000..ebcd37e7 --- /dev/null +++ b/src/action.c @@ -0,0 +1,41 @@ +/* + * Copyright © 2005 Novell, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without + * fee, provided that the above copyright notice appear in all copies + * and that both that copyright notice and this permission notice + * appear in supporting documentation, and that the name of + * Novell, Inc. not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior permission. + * Novell, Inc. makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + * + * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN + * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Author: David Reveman <davidr@novell.com> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <compiz.h> + +typedef struct _CompAction { + char *name; + char *description; + CompOptionType type; + union { + Bool b; + int i; + float f; + } value; +} CompAction; diff --git a/src/display.c b/src/display.c new file mode 100644 index 00000000..57e090cc --- /dev/null +++ b/src/display.c @@ -0,0 +1,1844 @@ +/* + * Copyright © 2005 Novell, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without + * fee, provided that the above copyright notice appear in all copies + * and that both that copyright notice and this permission notice + * appear in supporting documentation, and that the name of + * Novell, Inc. not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior permission. + * Novell, Inc. makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + * + * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN + * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Author: David Reveman <davidr@novell.com> + */ + +#ifdef HAVE_CONFIG_H +# include "../config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/poll.h> +#include <unistd.h> + +#define XK_MISCELLANY +#include <X11/keysymdef.h> + +#include <X11/Xlib.h> +#include <X11/Xatom.h> +#include <X11/extensions/Xcomposite.h> +#include <X11/extensions/Xrandr.h> +/* #include <X11/extensions/Xevie.h> */ +#include <X11/extensions/shape.h> + +#include <compiz.h> + +static unsigned int virtualModMask[] = { + CompAltMask, CompMetaMask, CompSuperMask, CompHyperMask, + CompModeSwitchMask, CompNumLockMask, CompScrollLockMask +}; + +typedef struct _CompTimeout { + struct _CompTimeout *next; + int time; + int left; + CallBackProc callBack; + void *closure; + CompTimeoutHandle handle; +} CompTimeout; + +static CompTimeout *timeouts = 0; +static struct timeval lastTimeout; +static CompTimeoutHandle lastTimeoutHandle = 1; + +#define CLICK_TO_FOCUS_DEFAULT TRUE + +#define AUTORAISE_DEFAULT TRUE + +#define AUTORAISE_DELAY_DEFAULT 1000 +#define AUTORAISE_DELAY_MIN 0 +#define AUTORAISE_DELAY_MAX 10000 + +#define NUM_OPTIONS(d) (sizeof ((d)->opt) / sizeof (CompOption)) + +static char *textureFilter[] = { "Fast", "Good", "Best" }; + +#define NUM_TEXTURE_FILTER (sizeof (textureFilter) / sizeof (textureFilter[0])) + +CompDisplay *compDisplays = 0; + +static CompDisplay compDisplay; + +static char *displayPrivateIndices = 0; +static int displayPrivateLen = 0; + +static int +reallocDisplayPrivate (int size, + void *closure) +{ + CompDisplay *d = compDisplays; + void *privates; + + if (d) + { + privates = realloc (d->privates, size * sizeof (CompPrivate)); + if (!privates) + return FALSE; + + d->privates = (CompPrivate *) privates; + } + + return TRUE; +} + +int +allocateDisplayPrivateIndex (void) +{ + return allocatePrivateIndex (&displayPrivateLen, + &displayPrivateIndices, + reallocDisplayPrivate, + 0); +} + +void +freeDisplayPrivateIndex (int index) +{ + freePrivateIndex (displayPrivateLen, displayPrivateIndices, index); +} + +static void +compDisplayInitOptions (CompDisplay *display, + char **plugin, + int nPlugin) +{ + CompOption *o; + int i; + + o = &display->opt[COMP_DISPLAY_OPTION_ACTIVE_PLUGINS]; + o->name = "active_plugins"; + o->shortDesc = "Active Plugins"; + o->longDesc = "List of currently active plugins"; + o->type = CompOptionTypeList; + o->value.list.type = CompOptionTypeString; + o->value.list.nValue = nPlugin; + o->value.list.value = malloc (sizeof (CompOptionValue) * nPlugin); + for (i = 0; i < nPlugin; i++) + o->value.list.value[i].s = strdup (plugin[i]); + o->rest.s.string = 0; + o->rest.s.nString = 0; + + display->dirtyPluginList = TRUE; + + o = &display->opt[COMP_DISPLAY_OPTION_TEXTURE_FILTER]; + o->name = "texture_filter"; + o->shortDesc = "Texture Filter"; + o->longDesc = "Texture filtering"; + o->type = CompOptionTypeString; + o->value.s = strdup (defaultTextureFilter); + o->rest.s.string = textureFilter; + o->rest.s.nString = NUM_TEXTURE_FILTER; + + o = &display->opt[COMP_DISPLAY_OPTION_CLICK_TO_FOCUS]; + o->name = "click_to_focus"; + o->shortDesc = "Click To Focus"; + o->longDesc = "Click on window moves input focus to it"; + o->type = CompOptionTypeBool; + o->value.b = CLICK_TO_FOCUS_DEFAULT; + + o = &display->opt[COMP_DISPLAY_OPTION_AUTORAISE]; + o->name = "autoraise"; + o->shortDesc = "Auto-Raise"; + o->longDesc = "Raise selected windows after interval"; + o->type = CompOptionTypeBool; + o->value.b = AUTORAISE_DEFAULT; + + o = &display->opt[COMP_DISPLAY_OPTION_AUTORAISE_DELAY]; + o->name = "autoraise_delay"; + o->shortDesc = "Auto-Raise Delay"; + o->longDesc = "Interval before raising selected windows"; + o->type = CompOptionTypeInt; + o->value.i = AUTORAISE_DELAY_DEFAULT; + o->rest.i.min = AUTORAISE_DELAY_MIN; + o->rest.i.max = AUTORAISE_DELAY_MAX; +} + +CompOption * +compGetDisplayOptions (CompDisplay *display, + int *count) +{ + *count = NUM_OPTIONS (display); + return display->opt; +} + +static Bool +setDisplayOption (CompDisplay *display, + char *name, + CompOptionValue *value) +{ + CompOption *o; + int index; + + o = compFindOption (display->opt, NUM_OPTIONS (display), name, &index); + if (!o) + return FALSE; + + switch (index) { + case COMP_DISPLAY_OPTION_ACTIVE_PLUGINS: + if (compSetOptionList (o, value)) + { + display->dirtyPluginList = TRUE; + return TRUE; + } + break; + case COMP_DISPLAY_OPTION_TEXTURE_FILTER: + if (compSetStringOption (o, value)) + { + CompScreen *s; + + for (s = display->screens; s; s = s->next) + damageScreen (s); + + if (strcmp (o->value.s, "Fast") == 0) + display->textureFilter = GL_NEAREST; + else + display->textureFilter = GL_LINEAR; + + return TRUE; + } + break; + case COMP_DISPLAY_OPTION_CLICK_TO_FOCUS: + if (compSetBoolOption (o, value)) + return TRUE; + break; + case COMP_DISPLAY_OPTION_AUTORAISE: + if (compSetBoolOption (o, value)) + return TRUE; + break; + case COMP_DISPLAY_OPTION_AUTORAISE_DELAY: + if (compSetIntOption (o, value)) + return TRUE; + default: + break; + } + + return FALSE; +} + +static Bool +setDisplayOptionForPlugin (CompDisplay *display, + char *plugin, + char *name, + CompOptionValue *value) +{ + CompPlugin *p; + + p = findActivePlugin (plugin); + if (p && p->vTable->setDisplayOption) + return (*p->vTable->setDisplayOption) (display, name, value); + + return FALSE; +} + +static Bool +initPluginForDisplay (CompPlugin *p, + CompDisplay *d) +{ + return (*p->vTable->initDisplay) (p, d); +} + +static void +finiPluginForDisplay (CompPlugin *p, + CompDisplay *d) +{ + (*p->vTable->finiDisplay) (p, d); +} + +static void +updatePlugins (CompDisplay *d) +{ + CompOption *o; + CompPlugin *p, **pop = 0; + int nPop, i, j; + + d->dirtyPluginList = FALSE; + + o = &d->opt[COMP_DISPLAY_OPTION_ACTIVE_PLUGINS]; + for (i = 0; i < d->plugin.list.nValue && i < o->value.list.nValue; i++) + { + if (strcmp (d->plugin.list.value[i].s, o->value.list.value[i].s)) + break; + } + + nPop = d->plugin.list.nValue - i; + + if (nPop) + { + pop = malloc (sizeof (CompPlugin *) * nPop); + if (!pop) + { + (*d->setDisplayOption) (d, o->name, &d->plugin); + return; + } + } + + for (j = 0; j < nPop; j++) + { + pop[j] = popPlugin (); + d->plugin.list.nValue--; + free (d->plugin.list.value[d->plugin.list.nValue].s); + } + + for (; i < o->value.list.nValue; i++) + { + p = 0; + for (j = 0; j < nPop; j++) + { + if (pop[j] && strcmp (pop[j]->vTable->name, + o->value.list.value[i].s) == 0) + { + if (pushPlugin (pop[j])) + { + p = pop[j]; + pop[j] = 0; + break; + } + } + } + + if (p == 0) + { + p = loadPlugin (o->value.list.value[i].s); + if (p) + { + if (!pushPlugin (p)) + { + unloadPlugin (p); + p = 0; + } + } + } + + if (p) + { + CompOptionValue *value; + + value = realloc (d->plugin.list.value, sizeof (CompOption) * + (d->plugin.list.nValue + 1)); + if (value) + { + value[d->plugin.list.nValue].s = strdup (p->vTable->name); + + d->plugin.list.value = value; + d->plugin.list.nValue++; + } + else + { + p = popPlugin (); + unloadPlugin (p); + } + } + } + + for (j = 0; j < nPop; j++) + { + if (pop[j]) + unloadPlugin (pop[j]); + } + + if (nPop) + free (pop); + + (*d->setDisplayOption) (d, o->name, &d->plugin); +} + +static void +addTimeout (CompTimeout *timeout) +{ + CompTimeout *p = 0, *t; + + for (t = timeouts; t; t = t->next) + { + if (timeout->time < t->left) + break; + + p = t; + } + + timeout->next = t; + timeout->left = timeout->time; + + if (p) + p->next = timeout; + else + timeouts = timeout; +} + +CompTimeoutHandle +compAddTimeout (int time, + CallBackProc callBack, + void *closure) +{ + CompTimeout *timeout; + + timeout = malloc (sizeof (CompTimeout)); + if (!timeout) + return 0; + + timeout->time = time; + timeout->callBack = callBack; + timeout->closure = closure; + timeout->handle = lastTimeoutHandle++; + + if (lastTimeoutHandle == MAXSHORT) + lastTimeoutHandle = 1; + + if (!timeouts) + gettimeofday (&lastTimeout, 0); + + addTimeout (timeout); + + return timeout->handle; +} + +void +compRemoveTimeout (CompTimeoutHandle handle) +{ + CompTimeout *p = 0, *t; + + for (t = timeouts; t; t = t->next) + { + if (t->handle == handle) + break; + + p = t; + } + + if (t) + { + if (p) + p->next = t->next; + else + timeouts = t->next; + + free (t); + } +} + +#define TIMEVALDIFF(tv1, tv2) \ + ((tv1)->tv_sec == (tv2)->tv_sec || (tv1)->tv_usec >= (tv2)->tv_usec) ? \ + ((((tv1)->tv_sec - (tv2)->tv_sec) * 1000000) + \ + ((tv1)->tv_usec - (tv2)->tv_usec)) / 1000 : \ + ((((tv1)->tv_sec - 1 - (tv2)->tv_sec) * 1000000) + \ + (1000000 + (tv1)->tv_usec - (tv2)->tv_usec)) / 1000 + +static int +getTimeToNextRedraw (CompScreen *s, + struct timeval *lastTv, + Bool idle) +{ + struct timeval tv; + int diff, next; + static int timeMult = 1; + + gettimeofday (&tv, 0); + + diff = TIMEVALDIFF (&tv, lastTv); + + if (idle) + { + if (timeMult > 1) + { + s->frameStatus = -1; + s->redrawTime = s->optimalRedrawTime; + timeMult--; + } + } + else + { + if (diff > s->redrawTime) + { + if (s->frameStatus > 0) + s->frameStatus = 0; + + next = s->optimalRedrawTime * (timeMult + 1); + if (diff > next) + { + s->frameStatus--; + if (s->frameStatus < -1) + { + timeMult++; + s->redrawTime = diff = next; + } + } + } + else if (diff < s->redrawTime) + { + if (s->frameStatus < 0) + s->frameStatus = 0; + + if (timeMult > 1) + { + next = s->optimalRedrawTime * (timeMult - 1); + if (diff < next) + { + s->frameStatus++; + if (s->frameStatus > 4) + { + timeMult--; + s->redrawTime = next; + } + } + } + } + } + + if (diff > s->redrawTime) + return 0; + + return s->redrawTime - diff; +} + +static CompWindow * +findWindowAt (CompDisplay *d, + Window root, + int x, + int y) +{ + CompScreen *s; + CompWindow *w; + + for (s = d->screens; s; s = s->next) + { + if (s->root == root && s->maxGrab == 0) + { + for (w = s->reverseWindows; w; w = w->prev) + { + if (x >= w->attrib.x && + y >= w->attrib.y && + x < w->attrib.x + w->width && + y < w->attrib.y + w->height) + return w; + } + } + } + + return 0; +} + +static Window +translateToRootWindow (CompDisplay *d, + Window child) +{ + CompScreen *s; + + for (s = d->screens; s; s = s->next) + { + if (s->root == child || s->grabWindow == child) + return s->root; + } + + return child; +} + +void +updateModifierMappings (CompDisplay *d) +{ + XModifierKeymap *modmap; + unsigned int modMask[CompModNum]; + int i, minKeycode, maxKeycode, keysymsPerKeycode = 0; + + for (i = 0; i < CompModNum; i++) + modMask[i] = 0; + + XDisplayKeycodes (d->display, &minKeycode, &maxKeycode); + XGetKeyboardMapping (d->display, minKeycode, (maxKeycode - minKeycode + 1), + &keysymsPerKeycode); + + modmap = XGetModifierMapping (d->display); + if (modmap && modmap->max_keypermod > 0) + { + static int maskTable[] = { + ShiftMask, LockMask, ControlMask, Mod1Mask, + Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask + }; + KeySym keysym; + int index, size, mask; + + size = (sizeof (maskTable) / sizeof (int)) * modmap->max_keypermod; + + for (i = 0; i < size; i++) + { + if (!modmap->modifiermap[i]) + continue; + + index = 0; + do + { + keysym = XKeycodeToKeysym (d->display, + modmap->modifiermap[i], + index++); + } while (!keysym && index < keysymsPerKeycode); + + if (keysym) + { + mask = maskTable[i / modmap->max_keypermod]; + + if (keysym == XK_Alt_L || + keysym == XK_Alt_R) + { + modMask[CompModAlt] |= mask; + } + else if (keysym == XK_Meta_L || + keysym == XK_Meta_R) + { + modMask[CompModMeta] |= mask; + } + else if (keysym == XK_Super_L || + keysym == XK_Super_R) + { + modMask[CompModSuper] |= mask; + } + else if (keysym == XK_Hyper_L || + keysym == XK_Hyper_R) + { + modMask[CompModHyper] |= mask; + } + else if (keysym == XK_Mode_switch) + { + modMask[CompModModeSwitch] |= mask; + } + else if (keysym == XK_Scroll_Lock) + { + modMask[CompModScrollLock] |= mask; + } + else if (keysym == XK_Num_Lock) + { + modMask[CompModNumLock] |= mask; + } + } + } + + if (modmap) + XFreeModifiermap (modmap); + + for (i = 0; i < CompModNum; i++) + { + if (!modMask[i]) + modMask[i] = CompNoMask; + } + + if (memcmp (modMask, d->modMask, sizeof (modMask))) + { + CompScreen *s; + + memcpy (d->modMask, modMask, sizeof (modMask)); + + d->ignoredModMask = LockMask | + (modMask[CompModNumLock] & ~CompNoMask) | + (modMask[CompModScrollLock] & ~CompNoMask); + + for (s = d->screens; s; s = s->next) + updatePassiveGrabs (s); + } + } +} + +unsigned int +virtualToRealModMask (CompDisplay *d, + unsigned int modMask) +{ + int i; + + for (i = 0; i < CompModNum; i++) + { + if (modMask & virtualModMask[i]) + { + modMask &= ~virtualModMask[i]; + modMask |= d->modMask[i]; + } + } + + return (modMask & ~(CompPressMask | CompReleaseMask)); +} + +static unsigned int +realToVirtualModMask (CompDisplay *d, + unsigned int modMask) +{ + int i; + + for (i = 0; i < CompModNum; i++) + { + if (modMask & d->modMask[i]) + modMask |= virtualModMask[i]; + } + + return modMask; +} + +void +eventLoop (void) +{ + XEvent event; + struct pollfd ufd; + int timeDiff; + struct timeval tv; + Region tmpRegion; + CompDisplay *display = compDisplays; + CompScreen *s = display->screens; + int timeToNextRedraw = 0; + CompWindow *move = 0; + int px = 0, py = 0; + int moveX = 0, moveY = 0; + CompTimeout *t; + Bool idle = TRUE; + + tmpRegion = XCreateRegion (); + if (!tmpRegion) + { + fprintf (stderr, "%s: Couldn't create region\n", programName); + return; + } + + ufd.fd = ConnectionNumber (display->display); + ufd.events = POLLIN; + + for (;;) + { + if (display->dirtyPluginList) + updatePlugins (display); + + if (restartSignal) + { + execvp (programName, programArgv); + exit (1); + } + + while (XPending (display->display)) + { + XNextEvent (display->display, &event); + + /* translate root window coordinates */ + if (testMode) + { + Window root, child; + + switch (event.type) { + case ButtonPress: + if (!move) + { + px = event.xbutton.x; + py = event.xbutton.y; + + move = findWindowAt (display, event.xbutton.window, + px, py); + if (move) + { + moveX = move->attrib.x; + moveY = move->attrib.y; + XRaiseWindow (display->display, move->id); + continue; + } + } + /* fall-through */ + case ButtonRelease: + move = 0; + + root = translateToRootWindow (display, + event.xbutton.window); + XTranslateCoordinates (display->display, + event.xbutton.root, root, + event.xbutton.x_root, + event.xbutton.y_root, + &event.xbutton.x_root, + &event.xbutton.y_root, + &child); + event.xbutton.root = root; + break; + case KeyPress: + case KeyRelease: + root = translateToRootWindow (display, event.xkey.window); + XTranslateCoordinates (display->display, + event.xkey.root, root, + event.xkey.x_root, + event.xkey.y_root, + &event.xkey.x_root, + &event.xkey.y_root, + &child); + event.xkey.root = root; + break; + case MotionNotify: + if (move) + { + moveX += event.xbutton.x - px; + moveY += event.xbutton.y - py; + px = event.xbutton.x; + py = event.xbutton.y; + + XMoveWindow (display->display, move->id, moveX, moveY); + + continue; + } + + root = translateToRootWindow (display, + event.xmotion.window); + XTranslateCoordinates (display->display, + event.xmotion.root, root, + event.xmotion.x_root, + event.xmotion.y_root, + &event.xmotion.x_root, + &event.xmotion.y_root, + &child); + event.xmotion.root = root; + default: + break; + } + } + + /* add virtual modifiers */ + switch (event.type) { + case ButtonPress: + event.xbutton.state |= CompPressMask; + event.xbutton.state = + realToVirtualModMask (display, event.xbutton.state); + break; + case ButtonRelease: + event.xbutton.state |= CompReleaseMask; + event.xbutton.state = + realToVirtualModMask (display, event.xbutton.state); + break; + case KeyPress: + event.xkey.state |= CompPressMask; + event.xkey.state = realToVirtualModMask (display, + event.xkey.state); + break; + case KeyRelease: + event.xkey.state |= CompReleaseMask; + event.xkey.state = realToVirtualModMask (display, + event.xkey.state); + break; + case MotionNotify: + event.xmotion.state = + realToVirtualModMask (display, event.xmotion.state); + break; + default: + break; + } + + sn_display_process_event (display->snDisplay, &event); + + (*display->handleEvent) (display, &event); + } + + if (s->damageMask) + { + /* sync with server */ + glFinish (); + + timeToNextRedraw = getTimeToNextRedraw (s, &s->lastRedraw, idle); + if (timeToNextRedraw) + timeToNextRedraw = poll (&ufd, 1, timeToNextRedraw); + + if (timeToNextRedraw == 0) + { + gettimeofday (&tv, 0); + + timeDiff = TIMEVALDIFF (&tv, &s->lastRedraw); + + s->stencilRef = 0; + + (*s->preparePaintScreen) (s, idle ? s->redrawTime : timeDiff); + + if (s->damageMask & COMP_SCREEN_DAMAGE_REGION_MASK) + { + XIntersectRegion (s->damage, &s->region, tmpRegion); + + if (tmpRegion->numRects == 1 && + tmpRegion->rects->x1 == 0 && + tmpRegion->rects->y1 == 0 && + tmpRegion->rects->x2 == s->width && + tmpRegion->rects->y2 == s->height) + damageScreen (s); + } + + EMPTY_REGION (s->damage); + + if (s->damageMask & COMP_SCREEN_DAMAGE_ALL_MASK) + { + s->damageMask = 0; + + (*s->paintScreen) (s, + &defaultScreenPaintAttrib, + &s->region, + PAINT_SCREEN_REGION_MASK | + PAINT_SCREEN_FULL_MASK); + + glXSwapBuffers (s->display->display, s->root); + } + else if (s->damageMask & COMP_SCREEN_DAMAGE_REGION_MASK) + { + s->damageMask = 0; + + if ((*s->paintScreen) (s, + &defaultScreenPaintAttrib, + tmpRegion, + PAINT_SCREEN_REGION_MASK)) + { + BoxPtr pBox; + int nBox, y; + + /* + pBox = tmpRegion->rects; + nBox = tmpRegion->numRects; + while (nBox--) + { + y = s->height - pBox->y2; + + glXCopySubBufferMESA (s->display->display, + s->root, + pBox->x1, y, + pBox->x2 - pBox->x1, + pBox->y2 - pBox->y1); + + pBox++; + } + + */ /* ugly empty rect flush hack */ /* + glXCopySubBufferMESA (s->display->display, s->root, + 0, 0, 0, 0); + */ + + glEnable (GL_SCISSOR_TEST); + glDrawBuffer (GL_FRONT); + + pBox = tmpRegion->rects; + nBox = tmpRegion->numRects; + while (nBox--) + { + y = s->height - pBox->y2; + + glBitmap (0, 0, 0, 0, + pBox->x1 - s->rasterX, y - s->rasterY, + NULL); + + s->rasterX = pBox->x1; + s->rasterY = y; + + glScissor (pBox->x1, y, + pBox->x2 - pBox->x1, + pBox->y2 - pBox->y1); + + glCopyPixels (pBox->x1, y, + pBox->x2 - pBox->x1, + pBox->y2 - pBox->y1, + GL_COLOR); + + pBox++; + } + + glDrawBuffer (GL_BACK); + glDisable (GL_SCISSOR_TEST); + glFlush (); + } + else + { + (*s->paintScreen) (s, + &defaultScreenPaintAttrib, + &s->region, + PAINT_SCREEN_FULL_MASK); + + glXSwapBuffers (s->display->display, s->root); + } + } + + s->lastRedraw = tv; + + (*s->donePaintScreen) (s); + + /* remove destroyed windows */ + while (s->pendingDestroys) + { + CompWindow *w; + + for (w = s->windows; w; w = w->next) + { + if (w->destroyed) + { + addWindowDamage (w); + removeWindow (w); + break; + } + } + + s->pendingDestroys--; + } + } + + idle = FALSE; + } + else + { + if (timeouts) + { + if (timeouts->left > 0) + poll (&ufd, 1, timeouts->left); + + gettimeofday (&tv, 0); + + timeDiff = TIMEVALDIFF (&tv, &lastTimeout); + + for (t = timeouts; t; t = t->next) + t->left -= timeDiff; + + while (timeouts && timeouts->left <= 0) + { + t = timeouts; + if ((*t->callBack) (t->closure)) + { + timeouts = t->next; + addTimeout (t); + } + else + { + timeouts = t->next; + free (t); + } + } + + lastTimeout = tv; + } + else + { + poll (&ufd, 1, 1000); + } + + idle = TRUE; + } + } +} + +static int errors = 0; + +static int +errorHandler (Display *dpy, + XErrorEvent *e) +{ + +#ifdef DEBUG + char str[128]; + char *name = 0; + int o; +#endif + + errors++; + +#ifdef DEBUG + XGetErrorDatabaseText (dpy, "XlibMessage", "XError", "", str, 128); + fprintf (stderr, "%s", str); + + o = e->error_code - compDisplays->damageError; + switch (o) { + case BadDamage: + name = "BadDamage"; + break; + default: + break; + } + + if (name) + { + fprintf (stderr, ": %s\n ", name); + } + else + { + XGetErrorText (dpy, e->error_code, str, 128); + fprintf (stderr, ": %s\n ", str); + } + + XGetErrorDatabaseText (dpy, "XlibMessage", "MajorCode", "%d", str, 128); + fprintf (stderr, str, e->request_code); + + sprintf (str, "%d", e->request_code); + XGetErrorDatabaseText (dpy, "XRequest", str, "", str, 128); + if (strcmp (str, "")) + fprintf (stderr, " (%s)", str); + fprintf (stderr, "\n "); + + XGetErrorDatabaseText (dpy, "XlibMessage", "MinorCode", "%d", str, 128); + fprintf (stderr, str, e->minor_code); + fprintf (stderr, "\n "); + + XGetErrorDatabaseText (dpy, "XlibMessage", "ResourceID", "%d", str, 128); + fprintf (stderr, str, e->resourceid); + fprintf (stderr, "\n"); + + /* abort (); */ +#endif + + return 0; +} + +int +compCheckForError (Display *dpy) +{ + int e; + + XSync (dpy, FALSE); + + e = errors; + errors = 0; + + return e; +} + +#define PING_DELAY 5000 + +static Bool +pingTimeout (void *closure) +{ + CompDisplay *d = closure; + CompScreen *s; + CompWindow *w; + XEvent ev; + int ping = d->lastPing + 1; + + ev.type = ClientMessage; + ev.xclient.window = 0; + ev.xclient.message_type = d->wmProtocolsAtom; + ev.xclient.format = 32; + ev.xclient.data.l[0] = d->wmPingAtom; + ev.xclient.data.l[1] = ping; + ev.xclient.data.l[2] = 0; + ev.xclient.data.l[3] = 0; + ev.xclient.data.l[4] = 0; + + for (s = d->screens; s; s = s->next) + { + for (w = s->windows; w; w = w->next) + { + if (w->attrib.map_state != IsViewable) + continue; + + if (!(w->type & CompWindowTypeNormalMask)) + continue; + + if (w->protocols & CompWindowProtocolPingMask) + { + if (w->transientFor) + continue; + + if (w->lastPong < d->lastPing) + { + if (w->alive) + { + w->alive = FALSE; + w->paint.saturation = 0; + + addWindowDamage (w); + } + } + + ev.xclient.window = w->id; + ev.xclient.data.l[2] = w->id; + + XSendEvent (d->display, w->id, FALSE, NoEventMask, &ev); + } + } + } + + d->lastPing = ping; + + return TRUE; +} + +Bool +addDisplay (char *name, + char **plugin, + int nPlugin) +{ + CompDisplay *d; + Display *dpy; + Window focus; + int revertTo, i; + + d = &compDisplay; + + if (displayPrivateLen) + { + d->privates = malloc (displayPrivateLen * sizeof (CompPrivate)); + if (!d->privates) + return FALSE; + } + else + d->privates = 0; + + d->screenPrivateIndices = 0; + d->screenPrivateLen = 0; + + for (i = 0; i < CompModNum; i++) + d->modMask[i] = CompNoMask; + + d->ignoredModMask = LockMask; + + d->plugin.list.type = CompOptionTypeString; + d->plugin.list.nValue = 0; + d->plugin.list.value = 0; + + compDisplayInitOptions (d, plugin, nPlugin); + + d->textureFilter = GL_LINEAR; + d->below = None; + + d->activeWindow = 0; + + d->autoRaiseHandle = 0; + d->autoRaiseWindow = None; + + d->display = dpy = XOpenDisplay (name); + if (!d->display) + { + fprintf (stderr, "%s: Couldn't open display %s\n", + programName, XDisplayName (name)); + return FALSE; + } + + snprintf (d->displayString, 255, "DISPLAY=%s", DisplayString (dpy)); + +#ifdef DEBUG + XSynchronize (dpy, TRUE); +#endif + + XSetErrorHandler (errorHandler); + + updateModifierMappings (d); + + d->setDisplayOption = setDisplayOption; + d->setDisplayOptionForPlugin = setDisplayOptionForPlugin; + + d->initPluginForDisplay = initPluginForDisplay; + d->finiPluginForDisplay = finiPluginForDisplay; + + d->handleEvent = handleEvent; + + d->supportedAtom = XInternAtom (dpy, "_NET_SUPPORTED", 0); + d->supportingWmCheckAtom = XInternAtom (dpy, "_NET_SUPPORTING_WM_CHECK", 0); + + d->utf8StringAtom = XInternAtom (dpy, "UTF8_STRING", 0); + + d->wmNameAtom = XInternAtom (dpy, "_NET_WM_NAME", 0); + + d->winTypeAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE", 0); + d->winTypeDesktopAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_DESKTOP", + 0); + d->winTypeDockAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_DOCK", 0); + d->winTypeToolbarAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_TOOLBAR", + 0); + d->winTypeMenuAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_MENU", 0); + d->winTypeUtilAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_UTILITY", + 0); + d->winTypeSplashAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_SPLASH", 0); + d->winTypeDialogAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_DIALOG", 0); + d->winTypeNormalAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_NORMAL", 0); + + d->winOpacityAtom = XInternAtom (dpy, "_NET_WM_WINDOW_OPACITY", 0); + d->winBrightnessAtom = XInternAtom (dpy, "_NET_WM_WINDOW_BRIGHTNESS", 0); + d->winSaturationAtom = XInternAtom (dpy, "_NET_WM_WINDOW_SATURATION", 0); + + d->winActiveAtom = XInternAtom (dpy, "_NET_ACTIVE_WINDOW", 0); + + d->workareaAtom = XInternAtom (dpy, "_NET_WORKAREA", 0); + + d->desktopViewportAtom = XInternAtom (dpy, "_NET_DESKTOP_VIEWPORT", 0); + d->desktopGeometryAtom = XInternAtom (dpy, "_NET_DESKTOP_GEOMETRY", 0); + d->currentDesktopAtom = XInternAtom (dpy, "_NET_CURRENT_DESKTOP", 0); + d->numberOfDesktopsAtom = XInternAtom (dpy, "_NET_NUMBER_OF_DESKTOPS", 0); + + d->winStateAtom = XInternAtom (dpy, "_NET_WM_STATE", 0); + d->winStateModalAtom = + XInternAtom (dpy, "_NET_WM_STATE_MODAL", 0); + d->winStateStickyAtom = + XInternAtom (dpy, "_NET_WM_STATE_STICKY", 0); + d->winStateMaximizedVertAtom = + XInternAtom (dpy, "_NET_WM_STATE_MAXIMIZED_VERT", 0); + d->winStateMaximizedHorzAtom = + XInternAtom (dpy, "_NET_WM_STATE_MAXIMIZED_HORZ", 0); + d->winStateShadedAtom = + XInternAtom (dpy, "_NET_WM_STATE_SHADED", 0); + d->winStateSkipTaskbarAtom = + XInternAtom (dpy, "_NET_WM_STATE_SKIP_TASKBAR", 0); + d->winStateSkipPagerAtom = + XInternAtom (dpy, "_NET_WM_STATE_SKIP_PAGER", 0); + d->winStateHiddenAtom = + XInternAtom (dpy, "_NET_WM_STATE_HIDDEN", 0); + d->winStateFullscreenAtom = + XInternAtom (dpy, "_NET_WM_STATE_FULLSCREEN", 0); + d->winStateAboveAtom = + XInternAtom (dpy, "_NET_WM_STATE_ABOVE", 0); + d->winStateBelowAtom = + XInternAtom (dpy, "_NET_WM_STATE_BELOW", 0); + d->winStateDemandsAttentionAtom = + XInternAtom (dpy, "_NET_WM_STATE_DEMANDS_ATTENTION", 0); + d->winStateDisplayModalAtom = + XInternAtom (dpy, "_NET_WM_STATE_DISPLAY_MODAL", 0); + + d->winActionMoveAtom = XInternAtom (dpy, "_NET_WM_ACTION_MOVE", 0); + d->winActionResizeAtom = XInternAtom (dpy, "_NET_WM_ACTION_RESIZE", 0); + d->winActionStickAtom = XInternAtom (dpy, "_NET_WM_ACTION_STICK", 0); + d->winActionMinimizeAtom = + XInternAtom (dpy, "_NET_WM_ACTION_MINIMIZE", 0); + d->winActionMaximizeHorzAtom = + XInternAtom (dpy, "_NET_WM_ACTION_MAXIMIZE_HORZ", 0); + d->winActionMaximizeVertAtom = + XInternAtom (dpy, "_NET_WM_ACTION_MAXIMIZE_VERT", 0); + d->winActionFullscreenAtom = + XInternAtom (dpy, "_NET_WM_ACTION_FULLSCREEN", 0); + d->winActionCloseAtom = XInternAtom (dpy, "_NET_WM_ACTION_CLOSE", 0); + + d->wmAllowedActionsAtom = XInternAtom (dpy, "_NET_WM_ALLOWED_ACTIONS", 0); + + d->wmStrutAtom = XInternAtom (dpy, "_NET_WM_STRUT", 0); + d->wmStrutPartialAtom = XInternAtom (dpy, "_NET_WM_STRUT_PARTIAL", 0); + + d->clientListAtom = XInternAtom (dpy, "_NET_CLIENT_LIST", 0); + d->clientListStackingAtom = + XInternAtom (dpy, "_NET_CLIENT_LIST_STACKING", 0); + + d->frameExtentsAtom = XInternAtom (dpy, "_NET_FRAME_EXTENTS", 0); + d->frameWindowAtom = XInternAtom (dpy, "_NET_FRAME_WINDOW", 0); + + d->wmStateAtom = XInternAtom (dpy, "WM_STATE", 0); + d->wmChangeStateAtom = XInternAtom (dpy, "WM_CHANGE_STATE", 0); + d->wmProtocolsAtom = XInternAtom (dpy, "WM_PROTOCOLS", 0); + d->wmClientLeaderAtom = XInternAtom (dpy, "WM_CLIENT_LEADER", 0); + + d->wmDeleteWindowAtom = XInternAtom (dpy, "WM_DELETE_WINDOW", 0); + d->wmTakeFocusAtom = XInternAtom (dpy, "WM_TAKE_FOCUS", 0); + d->wmPingAtom = XInternAtom (dpy, "_NET_WM_PING", 0); + d->wmSyncRequestAtom = XInternAtom (dpy, "_NET_WM_SYNC_REQUEST", 0); + + d->wmSyncRequestCounterAtom = + XInternAtom (dpy, "_NET_WM_SYNC_REQUEST_COUNTER", 0); + + d->closeWindowAtom = XInternAtom (dpy, "_NET_CLOSE_WINDOW", 0); + d->wmMoveResizeAtom = XInternAtom (dpy, "_NET_WM_MOVERESIZE", 0); + d->moveResizeWindowAtom = XInternAtom (dpy, "_NET_MOVERESIZE_WINDOW", 0); + + d->showingDesktopAtom = XInternAtom (dpy, "_NET_SHOWING_DESKTOP", 0); + + d->xBackgroundAtom[0] = XInternAtom (dpy, "_XSETROOT_ID", 0); + d->xBackgroundAtom[1] = XInternAtom (dpy, "_XROOTPMAP_ID", 0); + + d->panelActionAtom = XInternAtom (dpy, "_GNOME_PANEL_ACTION", 0); + d->panelActionMainMenuAtom = + XInternAtom (dpy, "_GNOME_PANEL_ACTION_MAIN_MENU", 0); + d->panelActionRunDialogAtom = + XInternAtom (dpy, "_GNOME_PANEL_ACTION_RUN_DIALOG", 0); + + d->mwmHintsAtom = XInternAtom (dpy, "_MOTIF_WM_HINTS", 0); + + d->managerAtom = XInternAtom (dpy, "MANAGER", 0); + d->targetsAtom = XInternAtom (dpy, "TARGETS", 0); + d->multipleAtom = XInternAtom (dpy, "MULTIPLE", 0); + d->timestampAtom = XInternAtom (dpy, "TIMESTAMP", 0); + d->versionAtom = XInternAtom (dpy, "VERSION", 0); + d->atomPairAtom = XInternAtom (dpy, "ATOM_PAIR", 0); + + d->snDisplay = sn_display_new (dpy, NULL, NULL); + if (!d->snDisplay) + return FALSE; + + d->lastPing = 1; + + if (testMode) + { + d->compositeOpcode = MAXSHORT; + d->compositeEvent = MAXSHORT; + d->compositeError = MAXSHORT; + + d->damageEvent = MAXSHORT; + d->damageError = MAXSHORT; + } + else + { + int compositeMajor, compositeMinor; + + if (!XQueryExtension (dpy, + COMPOSITE_NAME, + &d->compositeOpcode, + &d->compositeEvent, + &d->compositeError)) + { + fprintf (stderr, "%s: No composite extension\n", programName); + return FALSE; + } + + XCompositeQueryVersion (dpy, &compositeMajor, &compositeMinor); + if (compositeMajor == 0 && compositeMinor < 2) + { + fprintf (stderr, "%s: Old composite extension\n", programName); + return FALSE; + } + + if (!XDamageQueryExtension (dpy, &d->damageEvent, &d->damageError)) + { + fprintf (stderr, "%s: No damage extension\n", programName); + return FALSE; + } + + if (!XRRQueryExtension (dpy, &d->randrEvent, &d->randrError)) + { + fprintf (stderr, "%s: No RandR extension\n", programName); + return FALSE; + } + + if (!XSyncQueryExtension (dpy, &d->syncEvent, &d->syncError)) + { + fprintf (stderr, "%s: No sync extension\n", programName); + return FALSE; + } + } + + d->shapeExtension = XShapeQueryExtension (dpy, + &d->shapeEvent, + &d->shapeError); + + compDisplays = d; + + if (testMode) + { + addScreen (d, 0, None, 0, 0); + } + else + { + for (i = 0; i < ScreenCount (dpy); i++) + { + Window newWmSnOwner = None; + Atom wmSnAtom = 0; + Time wmSnTimestamp = 0; + XEvent event; + XSetWindowAttributes attr; + Window currentWmSnOwner; + char buf[128]; + + sprintf (buf, "WM_S%d", i); + wmSnAtom = XInternAtom (dpy, buf, 0); + + currentWmSnOwner = XGetSelectionOwner (dpy, wmSnAtom); + + if (currentWmSnOwner != None) + { + if (!replaceCurrentWm) + { + fprintf (stderr, + "%s: Screen %d on display \"%s\" already " + "has a window manager; try using the " + "--replace option to replace the current " + "window manager.\n", + programName, i, DisplayString (dpy)); + + continue; + } + + XSelectInput (dpy, currentWmSnOwner, + StructureNotifyMask); + } + + attr.override_redirect = TRUE; + attr.event_mask = PropertyChangeMask; + + newWmSnOwner = + XCreateWindow (dpy, XRootWindow (dpy, i), + -100, -100, 1, 1, 0, + CopyFromParent, CopyFromParent, + CopyFromParent, + CWOverrideRedirect | CWEventMask, + &attr); + + XChangeProperty (dpy, + newWmSnOwner, + d->wmNameAtom, + d->utf8StringAtom, 8, + PropModeReplace, + (unsigned char *) PACKAGE, + strlen (PACKAGE)); + + XWindowEvent (dpy, + newWmSnOwner, + PropertyChangeMask, + &event); + + wmSnTimestamp = event.xproperty.time; + + XSetSelectionOwner (dpy, wmSnAtom, newWmSnOwner, + wmSnTimestamp); + + if (XGetSelectionOwner (dpy, wmSnAtom) != newWmSnOwner) + { + fprintf (stderr, + "%s: Could not acquire window manager " + "selection on screen %d display \"%s\"\n", + programName, i, DisplayString (dpy)); + + XDestroyWindow (dpy, newWmSnOwner); + + continue; + } + + /* Send client message indicating that we are now the WM */ + event.xclient.type = ClientMessage; + event.xclient.window = XRootWindow (dpy, i); + event.xclient.message_type = d->managerAtom; + event.xclient.format = 32; + event.xclient.data.l[0] = wmSnTimestamp; + event.xclient.data.l[1] = wmSnAtom; + event.xclient.data.l[2] = 0; + event.xclient.data.l[3] = 0; + event.xclient.data.l[4] = 0; + + XSendEvent (dpy, XRootWindow (dpy, i), FALSE, + StructureNotifyMask, &event); + + /* Wait for old window manager to go away */ + if (currentWmSnOwner != None) + { + do { + XWindowEvent (dpy, currentWmSnOwner, + StructureNotifyMask, &event); + } while (event.type != DestroyNotify); + } + + compCheckForError (dpy); + + XCompositeRedirectSubwindows (dpy, XRootWindow (dpy, i), + CompositeRedirectManual); + + if (compCheckForError (dpy)) + { + fprintf (stderr, "%s: Another composite manager is already " + "running on screen: %d\n", programName, i); + + continue; + } + + XSelectInput (dpy, XRootWindow (dpy, i), + SubstructureRedirectMask | + SubstructureNotifyMask | + StructureNotifyMask | + PropertyChangeMask | + LeaveWindowMask | + EnterWindowMask | + KeyPressMask | + KeyReleaseMask | + FocusChangeMask | + ExposureMask); + + if (compCheckForError (dpy)) + { + fprintf (stderr, "%s: Another window manager is " + "already running on screen: %d\n", + programName, i); + + continue; + } + + if (!addScreen (d, i, newWmSnOwner, wmSnAtom, wmSnTimestamp)) + { + fprintf (stderr, "%s: Failed to manage screen: %d\n", + programName, i); + } + } + } + + if (!d->screens) + { + fprintf (stderr, "%s: No managable screens found on display %s\n", + programName, XDisplayName (name)); + return FALSE; + } + + XGetInputFocus (dpy, &focus, &revertTo); + + if (focus == None || focus == PointerRoot) + { + focusDefaultWindow (d); + } + else + { + CompWindow *w; + + w = findWindowAtDisplay (d, focus); + if (w) + { + moveInputFocusToWindow (w); + } + else + focusDefaultWindow (d); + } + + d->pingHandle = compAddTimeout (PING_DELAY, pingTimeout, d); + + return TRUE; +} + +void +focusDefaultWindow (CompDisplay *d) +{ + CompScreen *s; + CompWindow *w; + CompWindow *focus = NULL; + + for (s = d->screens; s; s = s->next) + { + for (w = s->reverseWindows; w; w = w->prev) + { + if (w->type & CompWindowTypeDockMask) + continue; + + if ((*s->focusWindow) (w)) + { + if (focus) + { + if (w->type & (CompWindowTypeNormalMask | + CompWindowTypeDialogMask | + CompWindowTypeModalDialogMask)) + { + if (w->activeNum > focus->activeNum) + focus = w; + } + } + else + focus = w; + } + } + } + + if (focus) + { + if (focus->id != d->activeWindow) + moveInputFocusToWindow (focus); + } + else + { + XSetInputFocus (d->display, d->screens->root, RevertToPointerRoot, + CurrentTime); + } +} + +CompScreen * +findScreenAtDisplay (CompDisplay *d, + Window root) +{ + CompScreen *s; + + for (s = d->screens; s; s = s->next) + { + if (s->root == root) + return s; + } + + return 0; +} + +void +forEachWindowOnDisplay (CompDisplay *display, + ForEachWindowProc proc, + void *closure) +{ + CompScreen *s; + + for (s = display->screens; s; s = s->next) + forEachWindowOnScreen (s, proc, closure); +} + +CompWindow * +findWindowAtDisplay (CompDisplay *d, + Window id) +{ + if (lastFoundWindow && lastFoundWindow->id == id) + { + return lastFoundWindow; + } + else + { + CompScreen *s; + CompWindow *w; + + for (s = d->screens; s; s = s->next) + { + w = findWindowAtScreen (s, id); + if (w) + return w; + } + } + + return 0; +} + +static CompScreen * +findScreenForSelection (CompDisplay *display, + Window owner, + Atom selection) +{ + CompScreen *s; + + for (s = display->screens; s; s = s->next) + { + if (s->wmSnSelectionWindow == owner && s->wmSnAtom == selection) + return s; + } + + return NULL; +} + +/* from fvwm2, Copyright Matthias Clasen, Dominik Vogt */ +static Bool +convertProperty (CompDisplay *display, + CompScreen *screen, + Window w, + Atom target, + Atom property) +{ + +#define N_TARGETS 4 + + Atom conversionTargets[N_TARGETS]; + long icccmVersion[] = { 2, 0 }; + + conversionTargets[0] = display->targetsAtom; + conversionTargets[1] = display->multipleAtom; + conversionTargets[2] = display->timestampAtom; + conversionTargets[3] = display->versionAtom; + + if (target == display->targetsAtom) + XChangeProperty (display->display, w, property, + XA_ATOM, 32, PropModeReplace, + (unsigned char *) conversionTargets, N_TARGETS); + else if (target == display->timestampAtom) + XChangeProperty (display->display, w, property, + XA_INTEGER, 32, PropModeReplace, + (unsigned char *) &screen->wmSnTimestamp, 1); + else if (target == display->versionAtom) + XChangeProperty (display->display, w, property, + XA_INTEGER, 32, PropModeReplace, + (unsigned char *) icccmVersion, 2); + else + return FALSE; + + /* Be sure the PropertyNotify has arrived so we + * can send SelectionNotify + */ + XSync (display->display, FALSE); + + return TRUE; +} + +/* from fvwm2, Copyright Matthias Clasen, Dominik Vogt */ +void +handleSelectionRequest (CompDisplay *display, + XEvent *event) +{ + XSelectionEvent reply; + CompScreen *screen; + + screen = findScreenForSelection (display, + event->xselectionrequest.owner, + event->xselectionrequest.selection); + if (!screen) + return; + + reply.type = SelectionNotify; + reply.display = display->display; + reply.requestor = event->xselectionrequest.requestor; + reply.selection = event->xselectionrequest.selection; + reply.target = event->xselectionrequest.target; + reply.property = None; + reply.time = event->xselectionrequest.time; + + if (event->xselectionrequest.target == display->multipleAtom) + { + if (event->xselectionrequest.property != None) + { + Atom type, *adata; + int i, format; + unsigned long num, rest; + unsigned char *data; + + if (XGetWindowProperty (display->display, + event->xselectionrequest.requestor, + event->xselectionrequest.property, + 0, 256, FALSE, + display->atomPairAtom, + &type, &format, &num, &rest, + &data) != Success) + return; + + /* FIXME: to be 100% correct, should deal with rest > 0, + * but since we have 4 possible targets, we will hardly ever + * meet multiple requests with a length > 8 + */ + adata = (Atom *) data; + i = 0; + while (i < (int) num) + { + if (!convertProperty (display, screen, + event->xselectionrequest.requestor, + adata[i], adata[i + 1])) + adata[i + 1] = None; + + i += 2; + } + + XChangeProperty (display->display, + event->xselectionrequest.requestor, + event->xselectionrequest.property, + display->atomPairAtom, + 32, PropModeReplace, data, num); + } + } + else + { + if (event->xselectionrequest.property == None) + event->xselectionrequest.property = event->xselectionrequest.target; + + if (convertProperty (display, screen, + event->xselectionrequest.requestor, + event->xselectionrequest.target, + event->xselectionrequest.property)) + reply.property = event->xselectionrequest.property; + } + + XSendEvent (display->display, + event->xselectionrequest.requestor, + FALSE, 0L, (XEvent *) &reply); +} + +void +handleSelectionClear (CompDisplay *display, + XEvent *event) +{ + /* We need to unmanage the screen on which we lost the selection */ + CompScreen *screen; + + screen = findScreenForSelection (display, + event->xselectionclear.window, + event->xselectionclear.selection); + + if (!screen) + return; + + /* removeScreen (screen); */ + + exit (0); +} diff --git a/src/event.c b/src/event.c new file mode 100644 index 00000000..f97f5581 --- /dev/null +++ b/src/event.c @@ -0,0 +1,1005 @@ +/* + * Copyright © 2005 Novell, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without + * fee, provided that the above copyright notice appear in all copies + * and that both that copyright notice and this permission notice + * appear in supporting documentation, and that the name of + * Novell, Inc. not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior permission. + * Novell, Inc. makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + * + * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN + * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Author: David Reveman <davidr@novell.com> + */ + +#include <stdlib.h> +#include <string.h> + +#include <X11/Xlib.h> +#include <X11/Xatom.h> +#include <X11/extensions/shape.h> +#include <X11/extensions/Xrandr.h> +#include <X11/extensions/Xevie.h> + +#include <compiz.h> + +static void +handleWindowDamageRect (CompWindow *w, + int x, + int y, + int width, + int height) +{ + REGION region; + Bool initial = FALSE; + + if (!w->damaged) + { + w->damaged = initial = TRUE; + w->invisible = WINDOW_INVISIBLE (w); + } + + region.extents.x1 = x; + region.extents.y1 = y; + region.extents.x2 = region.extents.x1 + width; + region.extents.y2 = region.extents.y1 + height; + + if (!(*w->screen->damageWindowRect) (w, initial, ®ion.extents)) + { + region.extents.x1 += w->attrib.x + w->attrib.border_width; + region.extents.y1 += w->attrib.y + w->attrib.border_width; + region.extents.x2 += w->attrib.x + w->attrib.border_width; + region.extents.y2 += w->attrib.y + w->attrib.border_width; + + region.rects = ®ion.extents; + region.numRects = region.size = 1; + + damageWindowRegion (w, ®ion); + + if (initial) + damageWindowOutputExtents (w); + } +} + +void +handleSyncAlarm (CompWindow *w) +{ + if (w->syncWait) + { + if (w->syncWaitHandle) + { + compRemoveTimeout (w->syncWaitHandle); + w->syncWaitHandle = 0; + } + + if (resizeWindow (w, + w->syncX, w->syncY, + w->syncWidth, w->syncHeight, + w->syncBorderWidth)) + { + XRectangle *rects; + int nDamage; + + nDamage = w->nDamage; + rects = w->damageRects; + while (nDamage--) + { + handleWindowDamageRect (w, + rects[nDamage].x, + rects[nDamage].y, + rects[nDamage].width, + rects[nDamage].height); + } + + w->nDamage = 0; + w->syncWait = FALSE; + } + } +} + +static void +moveInputFocusToOtherWindow (CompWindow *w) +{ + CompDisplay *display = w->screen->display; + + if (w->id == display->activeWindow) + { + CompWindow *ancestor; + + if (w->transientFor && w->transientFor != w->screen->root) + { + ancestor = findWindowAtDisplay (display, w->transientFor); + if (ancestor && !(ancestor->type & (CompWindowTypeDesktopMask | + CompWindowTypeDockMask))) + { + moveInputFocusToWindow (ancestor); + } + else + focusDefaultWindow (display); + } + else if (w->type & (CompWindowTypeDialogMask | + CompWindowTypeModalDialogMask)) + { + CompWindow *a, *focus = NULL; + + for (a = w->screen->reverseWindows; a; a = a->prev) + { + if (a->clientLeader == w->clientLeader) + { + if ((*w->screen->focusWindow) (a)) + { + if (focus) + { + if (a->type & (CompWindowTypeNormalMask | + CompWindowTypeDialogMask | + CompWindowTypeModalDialogMask)) + { + if (a->activeNum > focus->activeNum) + focus = a; + } + } + else + focus = a; + } + } + } + + if (focus && !(focus->type & (CompWindowTypeDesktopMask | + CompWindowTypeDockMask))) + { + moveInputFocusToWindow (focus); + } + else + focusDefaultWindow (display); + } + else + focusDefaultWindow (display); + } +} + +static Bool +autoRaiseTimeout (void *closure) +{ + CompDisplay *display = closure; + + if (display->autoRaiseWindow == display->activeWindow) + { + CompWindow *w; + + w = findWindowAtDisplay (display, display->autoRaiseWindow); + if (w) + updateWindowAttributes (w); + } + + return FALSE; +} + +void +handleEvent (CompDisplay *display, + XEvent *event) +{ + CompScreen *s; + CompWindow *w; + + switch (event->type) { + case Expose: + s = findScreenAtDisplay (display, event->xexpose.window); + if (s) + { + int more = event->xexpose.count + 1; + + if (s->nExpose == s->sizeExpose) + { + if (s->exposeRects) + { + s->exposeRects = realloc (s->exposeRects, + (s->sizeExpose + more) * + sizeof (XRectangle)); + s->sizeExpose += more; + } + else + { + s->exposeRects = malloc (more * sizeof (XRectangle)); + s->sizeExpose = more; + } + } + + s->exposeRects[s->nExpose].x = event->xexpose.x; + s->exposeRects[s->nExpose].y = event->xexpose.y; + s->exposeRects[s->nExpose].width = event->xexpose.width; + s->exposeRects[s->nExpose].height = event->xexpose.height; + s->nExpose++; + + if (event->xexpose.count == 0) + { + REGION rect; + + rect.rects = &rect.extents; + rect.numRects = rect.size = 1; + + while (s->nExpose--) + { + rect.extents.x1 = s->exposeRects[s->nExpose].x; + rect.extents.y1 = s->exposeRects[s->nExpose].y; + rect.extents.x2 = rect.extents.x1 + + s->exposeRects[s->nExpose].width; + rect.extents.y2 = rect.extents.y1 + + s->exposeRects[s->nExpose].height; + + damageScreenRegion (s, &rect); + } + s->nExpose = 0; + } + } + break; + case SelectionRequest: + handleSelectionRequest (display, event); + break; + case SelectionClear: + handleSelectionClear (display, event); + break; + case ConfigureNotify: + w = findWindowAtDisplay (display, event->xconfigure.window); + if (w) + { + configureWindow (w, &event->xconfigure); + } + else + { + s = findScreenAtDisplay (display, event->xconfigure.window); + if (s) + configureScreen (s, &event->xconfigure); + } + break; + case CreateNotify: + s = findScreenAtDisplay (display, event->xcreatewindow.parent); + if (s) + { + addWindow (s, event->xcreatewindow.window, + s->reverseWindows ? s->reverseWindows->id : 0); + } + break; + case DestroyNotify: + w = findWindowAtDisplay (display, event->xdestroywindow.window); + if (w) + { + destroyWindow (w); + moveInputFocusToOtherWindow (w); + } + break; + case MapNotify: + w = findWindowAtDisplay (display, event->xmap.window); + if (w) + mapWindow (w); + break; + case UnmapNotify: + w = findWindowAtDisplay (display, event->xunmap.window); + if (w) + { + unmapWindow (w); + moveInputFocusToOtherWindow (w); + } + break; + case ReparentNotify: + s = findScreenAtDisplay (display, event->xreparent.parent); + if (s) + { + addWindow (s, event->xreparent.window, 0); + } + else + { + w = findWindowAtDisplay (display, event->xreparent.window); + if (w) + { + destroyWindow (w); + moveInputFocusToOtherWindow (w); + } + } + break; + case CirculateNotify: + w = findWindowAtDisplay (display, event->xcirculate.window); + if (w) + circulateWindow (w, &event->xcirculate); + break; + case ButtonPress: + if (!display->screens->maxGrab) + XAllowEvents (display->display, ReplayPointer, event->xbutton.time); + + s = findScreenAtDisplay (display, event->xbutton.root); + if (s) + { + if (event->xbutton.button == Button1 || + event->xbutton.button == Button3) + { + w = findTopLevelWindowAtScreen (s, event->xbutton.window); + if (w) + activateWindow (w); + } + + if (EV_BUTTON (&s->opt[COMP_SCREEN_OPTION_CLOSE_WINDOW], event)) + closeActiveWindow (s); + + if (EV_BUTTON (&s->opt[COMP_SCREEN_OPTION_MAIN_MENU], event)) + panelAction (s, s->display->panelActionMainMenuAtom); + + if (EV_BUTTON (&s->opt[COMP_SCREEN_OPTION_RUN_DIALOG], event)) + panelAction (s, s->display->panelActionRunDialogAtom); + + if (EV_BUTTON (&s->opt[COMP_SCREEN_OPTION_RUN_COMMAND0], event)) + runCommand (s, s->opt[COMP_SCREEN_OPTION_COMMAND0].value.s); + + if (EV_BUTTON (&s->opt[COMP_SCREEN_OPTION_RUN_COMMAND1], event)) + runCommand (s, s->opt[COMP_SCREEN_OPTION_COMMAND1].value.s); + } + break; + case ButtonRelease: + break; + case KeyPress: + s = findScreenAtDisplay (display, event->xkey.root); + if (s) + { + if (EV_KEY (&s->opt[COMP_SCREEN_OPTION_CLOSE_WINDOW], event)) + closeActiveWindow (s); + + if (EV_KEY (&s->opt[COMP_SCREEN_OPTION_MAIN_MENU], event)) + panelAction (s, s->display->panelActionMainMenuAtom); + + if (EV_KEY (&s->opt[COMP_SCREEN_OPTION_RUN_DIALOG], event)) + panelAction (s, s->display->panelActionRunDialogAtom); + + if (EV_KEY (&s->opt[COMP_SCREEN_OPTION_RUN_COMMAND0], event)) + runCommand (s, s->opt[COMP_SCREEN_OPTION_COMMAND0].value.s); + + if (EV_KEY (&s->opt[COMP_SCREEN_OPTION_RUN_COMMAND1], event)) + runCommand (s, s->opt[COMP_SCREEN_OPTION_COMMAND1].value.s); + } + break; + case KeyRelease: + break; + case PropertyNotify: + if (event->xproperty.atom == display->winActiveAtom) + { + Window newActiveWindow; + + newActiveWindow = getActiveWindow (display, event->xproperty.window); + if (newActiveWindow != display->activeWindow) + { + display->activeWindow = newActiveWindow; + + s = findScreenAtDisplay (display, event->xproperty.window); + if (s) + { + w = findWindowAtDisplay (display, newActiveWindow); + if (w) + w->activeNum = s->activeNum++; + } + } + } + else if (event->xproperty.atom == display->winTypeAtom) + { + w = findWindowAtDisplay (display, event->xproperty.window); + if (w) + { + unsigned int type; + + type = getWindowType (display, w->id); + + if (type != w->wmType) + { + if (w->attrib.map_state == IsViewable) + { + if (w->type == CompWindowTypeDesktopMask) + w->screen->desktopWindowCount--; + else if (type == CompWindowTypeDesktopMask) + w->screen->desktopWindowCount++; + } + + w->wmType = type; + + recalcWindowType (w); + + if (w->type & CompWindowTypeDesktopMask) + w->paint.opacity = OPAQUE; + } + } + } + else if (event->xproperty.atom == display->winStateAtom) + { + w = findWindowAtDisplay (display, event->xproperty.window); + if (w) + { + unsigned int state; + + state = getWindowState (display, w->id); + + if (state != w->state) + { + w->state = state; + + recalcWindowType (w); + + if (w->type & CompWindowTypeDesktopMask) + w->paint.opacity = OPAQUE; + } + } + } + else if (event->xproperty.atom == XA_WM_NORMAL_HINTS) + { + w = findWindowAtDisplay (display, event->xproperty.window); + if (w) + updateNormalHints (w); + } + else if (event->xproperty.atom == XA_WM_HINTS) + { + w = findWindowAtDisplay (display, event->xproperty.window); + if (w) + updateWmHints (w); + } + else if (event->xproperty.atom == XA_WM_TRANSIENT_FOR) + { + w = findWindowAtDisplay (display, event->xproperty.window); + if (w) + XGetTransientForHint (display->display, + w->id, &w->transientFor); + } + else if (event->xproperty.atom == display->wmClientLeaderAtom) + { + w = findWindowAtDisplay (display, event->xproperty.window); + if (w) + w->clientLeader = getClientLeader (w); + } + else if (event->xproperty.atom == display->winOpacityAtom) + { + w = findWindowAtDisplay (display, event->xproperty.window); + if (w && (w->type & CompWindowTypeDesktopMask) == 0) + { + GLushort opacity; + + opacity = getWindowProp32 (display, w->id, + display->winOpacityAtom, + OPAQUE); + if (opacity != w->paint.opacity) + { + w->paint.opacity = opacity; + addWindowDamage (w); + } + } + } + else if (event->xproperty.atom == display->winBrightnessAtom) + { + w = findWindowAtDisplay (display, event->xproperty.window); + if (w) + { + GLushort brightness; + + brightness = getWindowProp32 (display, w->id, + display->winBrightnessAtom, + BRIGHT); + if (brightness != w->paint.brightness) + { + w->paint.brightness = brightness; + addWindowDamage (w); + } + } + } + else if (event->xproperty.atom == display->winSaturationAtom) + { + w = findWindowAtDisplay (display, event->xproperty.window); + if (w && w->screen->canDoSaturated) + { + GLushort saturation; + + saturation = getWindowProp32 (display, w->id, + display->winSaturationAtom, + COLOR); + if (saturation != w->saturation) + { + w->saturation = saturation; + if (w->alive) + { + w->paint.saturation = w->saturation; + addWindowDamage (w); + } + } + } + } + else if (event->xproperty.atom == display->xBackgroundAtom[0] || + event->xproperty.atom == display->xBackgroundAtom[1]) + { + s = findScreenAtDisplay (display, event->xproperty.window); + if (s) + { + finiTexture (s, &s->backgroundTexture); + initTexture (s, &s->backgroundTexture); + + damageScreen (s); + } + } + else if (event->xproperty.atom == display->wmStrutAtom || + event->xproperty.atom == display->wmStrutPartialAtom) + { + w = findWindowAtDisplay (display, event->xproperty.window); + if (w) + { + if (updateWindowStruts (w)) + updateWorkareaForScreen (w->screen); + } + } + else if (event->xproperty.atom == display->mwmHintsAtom) + { + w = findWindowAtDisplay (display, event->xproperty.window); + if (w) + { + w->mwmDecor = getMwmDecor (w->screen->display, w->id); + + updateWindowAttributes (w); + } + } + else if (event->xproperty.atom == display->wmProtocolsAtom) + { + w = findWindowAtDisplay (display, event->xproperty.window); + if (w) + w->protocols = getProtocols (w->screen->display, w->id); + } + break; + case MotionNotify: + break; + case ClientMessage: + if (event->xclient.message_type == display->winActiveAtom) + { + w = findWindowAtDisplay (display, event->xclient.window); + if (w) + activateWindow (w); + } + else if (event->xclient.message_type == display->winOpacityAtom) + { + w = findWindowAtDisplay (display, event->xclient.window); + if (w && (w->type & CompWindowTypeDesktopMask) == 0) + { + GLushort opacity; + + opacity = event->xclient.data.l[0] >> 16; + if (opacity != w->paint.opacity) + { + w->paint.opacity = opacity; + + setWindowProp32 (display, w->id, display->winOpacityAtom, + w->paint.opacity); + addWindowDamage (w); + } + } + } + else if (event->xclient.message_type == display->winBrightnessAtom) + { + w = findWindowAtDisplay (display, event->xclient.window); + if (w) + { + GLushort brightness; + + brightness = event->xclient.data.l[0] >> 16; + if (brightness != w->paint.brightness) + { + w->paint.brightness = brightness; + + setWindowProp32 (display, w->id, display->winBrightnessAtom, + w->paint.brightness); + addWindowDamage (w); + } + } + } + else if (event->xclient.message_type == display->winSaturationAtom) + { + w = findWindowAtDisplay (display, event->xclient.window); + if (w && w->screen->canDoSaturated) + { + GLushort saturation; + + saturation = event->xclient.data.l[0] >> 16; + if (saturation != w->saturation) + { + w->saturation = saturation; + + setWindowProp32 (display, w->id, display->winSaturationAtom, + w->saturation); + + if (w->alive) + { + w->paint.saturation = w->saturation; + addWindowDamage (w); + } + } + } + } + else if (event->xclient.message_type == display->winStateAtom) + { + w = findWindowAtDisplay (display, event->xclient.window); + if (w) + { + unsigned long wState, state; + int i; + + wState = w->state; + + for (i = 1; i < 3; i++) + { + state = windowStateMask (display, event->xclient.data.l[i]); + if (state & ~CompWindowStateHiddenMask) + { + +#define _NET_WM_STATE_REMOVE 0 +#define _NET_WM_STATE_ADD 1 +#define _NET_WM_STATE_TOGGLE 2 + + switch (event->xclient.data.l[0]) { + case _NET_WM_STATE_REMOVE: + wState &= ~state; + break; + case _NET_WM_STATE_ADD: + wState |= state; + break; + case _NET_WM_STATE_TOGGLE: + wState ^= state; + break; + } + } + } + + if (wState != w->state) + { + w->state = wState; + + recalcWindowType (w); + + if (!w->attrib.override_redirect) + updateWindowAttributes (w); + + setWindowState (display, wState, w->id); + } + } + } + else if (event->xclient.message_type == display->wmProtocolsAtom) + { + if (event->xclient.data.l[0] == display->wmPingAtom) + { + w = findWindowAtDisplay (display, event->xclient.data.l[2]); + if (w) + { + if (!w->alive) + { + w->alive = TRUE; + w->paint.saturation = w->saturation; + + addWindowDamage (w); + } + w->lastPong = display->lastPing; + } + } + } + else if (event->xclient.message_type == display->closeWindowAtom) + { + w = findWindowAtDisplay (display, event->xclient.window); + if (w) + closeWindow (w); + } + else if (event->xclient.message_type == display->desktopGeometryAtom) + { + s = findScreenAtDisplay (display, event->xclient.window); + if (s) + { + CompOptionValue value; + + value.i = event->xclient.data.l[0] / s->width; + + (*s->setScreenOption) (s, "size", &value); + } + } + else if (event->xclient.message_type == display->moveResizeWindowAtom) + { + unsigned int xwcm = 0; + XWindowChanges xwc; + + if (event->xclient.data.l[0] & (1 << 7)) + { + xwcm |= CWX; + xwc.x = event->xclient.data.l[1]; + } + + if (event->xclient.data.l[0] & (1 << 8)) + { + xwcm |= CWY; + xwc.y = event->xclient.data.l[2]; + } + + if (event->xclient.data.l[0] & (1 << 9)) + { + xwcm |= CWWidth; + xwc.width = event->xclient.data.l[3]; + } + + if (event->xclient.data.l[0] & (1 << 10)) + { + xwcm |= CWHeight; + xwc.height = event->xclient.data.l[4]; + } + + /* TODO: gravity */ + + if (xwcm & (CWX | CWY)) + { + w = findWindowAtDisplay (display, event->xclient.window); + if (w) + { + if (xwcm & CWX) + xwc.x += w->input.left; + + if (xwcm & CWY) + xwc.y += w->input.top; + } + } + + XConfigureWindow (display->display, + event->xclient.window, + xwcm, &xwc); + } + else if (event->xclient.message_type == display->wmChangeStateAtom) + { + w = findWindowAtDisplay (display, event->xclient.window); + if (w && w->type & CompWindowTypeNormalMask) + { + if (event->xclient.data.l[0] == IconicState) + minimizeWindow (w); + else if (event->xclient.data.l[0] == NormalState) + unminimizeWindow (w); + } + } + else if (event->xclient.message_type == display->showingDesktopAtom) + { + s = findScreenAtDisplay (display, event->xclient.window); + if (s) + { + if (event->xclient.data.l[0]) + enterShowDesktopMode (s); + else + leaveShowDesktopMode (s); + } + } + break; + case MappingNotify: + updateModifierMappings (display); + break; + case MapRequest: + w = findWindowAtDisplay (display, event->xmaprequest.window); + if (w) + { + if (w->minimized) + unminimizeWindow (w); + + if (w->screen->showingDesktopMask) + leaveShowDesktopMode (w->screen); + + if (!(w->state & CompWindowStateHiddenMask)) + { + XMapWindow (display->display, event->xmaprequest.window); + + updateWindowAttributes (w); + + if (!(w->type & (CompWindowTypeDesktopMask | + CompWindowTypeDockMask))) + moveInputFocusToWindow (w); + } + } + else + { + XMapWindow (display->display, event->xmaprequest.window); + } + break; + case ConfigureRequest: { + unsigned int xwcm; + XWindowChanges xwc; + + xwcm = event->xconfigurerequest.value_mask & + (CWX | CWY | CWWidth | CWHeight | CWBorderWidth); + + xwc.x = event->xconfigurerequest.x; + xwc.y = event->xconfigurerequest.y; + xwc.width = event->xconfigurerequest.width; + xwc.height = event->xconfigurerequest.height; + xwc.border_width = event->xconfigurerequest.border_width; + + /* TODO: gravity */ + + if (xwcm & (CWX | CWY)) + { + w = findWindowAtDisplay (display, event->xconfigurerequest.window); + if (w) + { + xwc.x += w->input.left; + xwc.y += w->input.top; + } + } + + XConfigureWindow (display->display, + event->xconfigurerequest.window, + xwcm, &xwc); + } break; + case CirculateRequest: + break; + case FocusIn: + if (event->xfocus.window != display->activeWindow && + event->xfocus.mode != NotifyGrab) + { + w = findWindowAtDisplay (display, event->xfocus.window); + if (w) + { + XChangeProperty (display->display, w->screen->root, + display->winActiveAtom, + XA_WINDOW, 32, PropModeReplace, + (unsigned char *) &w->id, 1); + } + } + break; + case EnterNotify: + if (!display->screens->maxGrab && + event->xcrossing.mode != NotifyGrab && + event->xcrossing.detail != NotifyInferior) + { + Bool raise; + int delay; + + raise = display->opt[COMP_DISPLAY_OPTION_AUTORAISE].value.b; + delay = display->opt[COMP_DISPLAY_OPTION_AUTORAISE_DELAY].value.i; + + s = findScreenAtDisplay (display, event->xcrossing.root); + if (s) + w = findTopLevelWindowAtScreen (s, event->xcrossing.window); + else + w = NULL; + + if (w && w->id != display->below) + { + display->below = w->id; + + if (!display->opt[COMP_DISPLAY_OPTION_CLICK_TO_FOCUS].value.b) + { + if (display->autoRaiseHandle && + display->autoRaiseWindow != w->id) + { + compRemoveTimeout (display->autoRaiseHandle); + display->autoRaiseHandle = 0; + } + + if (w->type & ~(CompWindowTypeDockMask | + CompWindowTypeDesktopMask)) + { + moveInputFocusToWindow (w); + + if (raise) + { + if (delay > 0) + { + display->autoRaiseWindow = w->id; + display->autoRaiseHandle = + compAddTimeout (delay, autoRaiseTimeout, + display); + } + else + updateWindowAttributes (w); + } + } + } + } + } + break; + case LeaveNotify: + if (event->xcrossing.detail != NotifyInferior) + { + if (event->xcrossing.window == display->below) + display->below = None; + } + break; + default: + if (event->type == display->damageEvent + XDamageNotify) + { + XDamageNotifyEvent *de = (XDamageNotifyEvent *) event; + + if (lastDamagedWindow && de->drawable == lastDamagedWindow->id) + { + w = lastDamagedWindow; + } + else + { + w = findWindowAtDisplay (display, de->drawable); + if (w) + lastDamagedWindow = w; + } + + if (w) + { + if (w->syncWait) + { + if (w->nDamage == w->sizeDamage) + { + if (w->damageRects) + { + w->damageRects = realloc (w->damageRects, + (w->sizeDamage + 1) * + sizeof (XRectangle)); + w->sizeDamage += 1; + } + else + { + w->damageRects = malloc (sizeof (XRectangle)); + w->sizeDamage = 1; + } + } + + w->damageRects[w->nDamage].x = de->area.x; + w->damageRects[w->nDamage].y = de->area.y; + w->damageRects[w->nDamage].width = de->area.width; + w->damageRects[w->nDamage].height = de->area.height; + w->nDamage++; + } + else + { + handleWindowDamageRect (w, + de->area.x, + de->area.y, + de->area.width, + de->area.height); + } + } + } + else if (display->shapeExtension && + event->type == display->shapeEvent + ShapeNotify) + { + w = findWindowAtDisplay (display, ((XShapeEvent *) event)->window); + if (w) + { + if (w->mapNum) + { + addWindowDamage (w); + updateWindowRegion (w); + addWindowDamage (w); + } + } + } + else if (event->type == display->randrEvent + RRScreenChangeNotify) + { + XRRScreenChangeNotifyEvent *rre; + + rre = (XRRScreenChangeNotifyEvent *) event; + + s = findScreenAtDisplay (display, rre->root); + if (s) + detectRefreshRateOfScreen (s); + } + else if (event->type == display->syncEvent + XSyncAlarmNotify) + { + XSyncAlarmNotifyEvent *sa; + + sa = (XSyncAlarmNotifyEvent *) event; + + w = NULL; + + for (s = display->screens; s; s = s->next) + for (w = s->windows; w; w = w->next) + if (w->syncAlarm == sa->alarm) + break; + + if (w) + handleSyncAlarm (w); + } + break; + } +} diff --git a/src/main.c b/src/main.c new file mode 100644 index 00000000..b715f42f --- /dev/null +++ b/src/main.c @@ -0,0 +1,211 @@ +/* + * Copyright © 2005 Novell, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without + * fee, provided that the above copyright notice appear in all copies + * and that both that copyright notice and this permission notice + * appear in supporting documentation, and that the name of + * Novell, Inc. not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior permission. + * Novell, Inc. makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + * + * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN + * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Author: David Reveman <davidr@novell.com> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <signal.h> +#include <unistd.h> +#include <string.h> +#include <sys/wait.h> + +#include <compiz.h> + +char *programName; +char **programArgv; +int programArgc; + +char *backgroundImage = "background.png"; +char *windowImage = "window.png"; + +REGION emptyRegion; +REGION infiniteRegion; +GLushort defaultColor[4] = { 0, 0, 0, 0 }; +Window currentRoot = 0; + +int defaultRefreshRate = 50; +char *defaultTextureFilter = "Good"; + +char *windowTypeString[] = { + "Desktop", + "Dock", + "Toolbar", + "Menu", + "Utility", + "Splash", + "Dialog", + "ModalDialog", + "Normal", + "Fullscreen", + "Unknown" +}; +int nWindowTypeString = + sizeof (windowTypeString) / sizeof (windowTypeString[0]); + +Bool testMode = FALSE; +Bool restartSignal = FALSE; + +CompWindow *lastFoundWindow = 0; +CompWindow *lastDamagedWindow = 0; + +Bool replaceCurrentWm = FALSE; + +static void +usage (void) +{ + printf ("Usage: %s " + "[--display DISPLAY] " + "[--bg-image PNG] " + "[--window-image PNG]\n " + "[--refresh-rate RATE] " + "[--fast-filter] " + "[--test-mode] " + "[--replace]\n " + "[--sm-disable] " + "[--sm-save-file] " + "[--sm-client-id] " + "[--help] " + "[PLUGIN]...\n", + programName); +} + +static void +signalHandler (int sig) +{ + int status; + + switch (sig) { + case SIGCHLD: + waitpid (-1, &status, WNOHANG | WUNTRACED); + break; + case SIGHUP: + restartSignal = TRUE; + default: + break; + } +} + +int +main (int argc, char **argv) +{ + char *displayName = 0; + char *plugin[256]; + int i, nPlugin = 0; + Bool disableSm = TRUE; + char *clientId = NULL; + char *saveFile = NULL; + + programName = argv[0]; + programArgc = argc; + programArgv = argv; + + signal (SIGHUP, signalHandler); + signal (SIGCHLD, signalHandler); + + emptyRegion.rects = &emptyRegion.extents; + emptyRegion.numRects = 0; + emptyRegion.extents.x1 = 0; + emptyRegion.extents.y1 = 0; + emptyRegion.extents.x2 = 0; + emptyRegion.extents.y2 = 0; + emptyRegion.size = 0; + + infiniteRegion.rects = &infiniteRegion.extents; + infiniteRegion.numRects = 1; + infiniteRegion.extents.x1 = MINSHORT; + infiniteRegion.extents.y1 = MINSHORT; + infiniteRegion.extents.x2 = MAXSHORT; + infiniteRegion.extents.y2 = MAXSHORT; + + for (i = 1; i < argc; i++) + { + if (!strcmp (argv[i], "--help")) + { + usage (); + return 0; + } + else if (!strcmp (argv[i], "--display")) + { + if (i + 1 < argc) + displayName = argv[++i]; + } + else if (!strcmp (argv[i], "--refresh-rate")) + { + if (i + 1 < argc) + { + defaultRefreshRate = atoi (programArgv[++i]); + defaultRefreshRate = RESTRICT_VALUE (defaultRefreshRate, + 1, 1000); + } + } + else if (!strcmp (argv[i], "--fast-filter")) + { + defaultTextureFilter = "Fast"; + } + else if (!strcmp (argv[i], "--test-mode")) + { + testMode = TRUE; + } + else if (!strcmp (argv[i], "--replace")) + { + replaceCurrentWm = TRUE; + } + else if (!strcmp (argv[i], "--sm-disable")) + { + disableSm = TRUE; + } + else if (!strcmp (argv[i], "--sm-client-id")) + { + if (i + 1 < argc) + clientId = argv[++i]; + } + else if (!strcmp (argv[i], "--sm-save-file")) + { + if (i + 1 < argc) + saveFile = argv[++i]; + } + else if (!strcmp (argv[i], "--bg-image")) + { + if (i + 1 < argc) + backgroundImage = argv[++i]; + } + else if (!strcmp (argv[i], "--window-image")) + { + if (i + 1 < argc) + windowImage = argv[++i]; + } + else + { + if (nPlugin < 256) + plugin[nPlugin++] = argv[i]; + } + } + + if (!addDisplay (displayName, plugin, nPlugin)) + return 1; + + eventLoop (); + + return 0; +} diff --git a/src/option.c b/src/option.c new file mode 100644 index 00000000..23999c66 --- /dev/null +++ b/src/option.c @@ -0,0 +1,292 @@ +/* + * Copyright © 2005 Novell, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without + * fee, provided that the above copyright notice appear in all copies + * and that both that copyright notice and this permission notice + * appear in supporting documentation, and that the name of + * Novell, Inc. not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior permission. + * Novell, Inc. makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + * + * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN + * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Author: David Reveman <davidr@novell.com> + */ + +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <math.h> + +#include <compiz.h> + +CompOption * +compFindOption (CompOption *option, + int nOption, + char *name, + int *index) +{ + int i; + + for (i = 0; i < nOption; i++) + { + if (strcmp (option[i].name, name) == 0) + { + if (index) + *index = i; + + return &option[i]; + } + } + + return 0; +} + +Bool +compSetBoolOption (CompOption *option, + CompOptionValue *value) +{ + option->value.i = (value->b) ? TRUE : FALSE; + + return TRUE; +} + +Bool +compSetIntOption (CompOption *option, + CompOptionValue *value) +{ + if (value->i < option->rest.i.min || + value->i > option->rest.i.max || + value->i == option->value.i) + return FALSE; + + option->value.i = value->i; + + return TRUE; +} + +Bool +compSetFloatOption (CompOption *option, + CompOptionValue *value) +{ + float v, p; + + p = 1.0f / option->rest.f.precision; + v = ((int) (value->f * p + 0.5f)) / p; + + if (v < option->rest.f.min || + v > option->rest.f.max || + v == option->value.f) + return FALSE; + + option->value.f = v; + + return TRUE; +} + +Bool +compSetStringOption (CompOption *option, + CompOptionValue *value) +{ + char *s; + + s = value->s; + if (!s) + s = ""; + + if (option->rest.s.nString) + { + int i; + + for (i = 0; i < option->rest.s.nString; i++) + { + if (strcmp (option->rest.s.string[i], s) == 0) + break; + } + + if (i == option->rest.s.nString) + s = option->rest.s.string[0]; + } + + if (option->value.s == s) + return FALSE; + + if (option->value.s && s) + { + if (strcmp (option->value.s, s) == 0) + return FALSE; + } + + if (option->value.s) + free (option->value.s); + + option->value.s = strdup (s); + + return TRUE; +} + +Bool +compSetColorOption (CompOption *option, + CompOptionValue *value) +{ + if (memcmp (value->c, option->value.c, sizeof (value->c)) == 0) + return FALSE; + + memcpy (option->value.c, value->c, sizeof (value->c)); + + return TRUE; +} + +Bool +compSetBindingOption (CompOption *option, + CompOptionValue *value) +{ + CompBinding *binding; + + binding = &option->value.bind; + if (value->bind.type == CompBindingTypeButton) + { + if (binding->type == CompBindingTypeButton && + binding->u.button.button == value->bind.u.button.button && + binding->u.button.modifiers == value->bind.u.button.modifiers) + return FALSE; + } + else + { + if (binding->type == CompBindingTypeKey && + binding->u.key.keycode == value->bind.u.key.keycode && + binding->u.key.modifiers == value->bind.u.key.modifiers) + return FALSE; + } + + *binding = value->bind; + + return TRUE; +} + +Bool +compSetOptionList (CompOption *option, + CompOptionValue *value) +{ + CompOption o; + Bool status = FALSE; + int i, min; + + if (value->list.nValue != option->value.list.nValue) + { + CompOptionValue *v; + + v = malloc (sizeof (CompOptionValue) * value->list.nValue); + if (!v) + return FALSE; + + min = MIN (value->list.nValue, option->value.list.nValue); + + if (min < option->value.list.nValue) + { + switch (option->value.list.type) { + case CompOptionTypeString: + for (i = min; i < option->value.list.nValue; i++) + { + if (option->value.list.value[i].s) + free (option->value.list.value[i].s); + } + default: + break; + } + } + + memset (v, 0, sizeof (CompOptionValue) * value->list.nValue); + + if (min) + memcpy (v, option->value.list.value, + sizeof (CompOptionValue) * min); + + if (option->value.list.value) + free (option->value.list.value); + + option->value.list.value = v; + option->value.list.nValue = value->list.nValue; + + status = TRUE; + } + + o = *option; + o.type = option->value.list.type; + + for (i = 0; i < value->list.nValue; i++) + { + o.value = option->value.list.value[i]; + + switch (o.type) { + case CompOptionTypeBool: + status |= compSetBoolOption (&o, &value->list.value[i]); + break; + case CompOptionTypeInt: + status |= compSetIntOption (&o, &value->list.value[i]); + break; + case CompOptionTypeFloat: + status |= compSetFloatOption (&o, &value->list.value[i]); + break; + case CompOptionTypeString: + status |= compSetStringOption (&o, &value->list.value[i]); + break; + case CompOptionTypeColor: + status |= compSetColorOption (&o, &value->list.value[i]); + break; + case CompOptionTypeBinding: + status |= compSetBindingOption (&o, &value->list.value[i]); + default: + break; + } + + option->value.list.value[i] = o.value; + } + + return status; +} + +unsigned int +compWindowTypeMaskFromStringList (CompOptionValue *value) +{ + int i; + unsigned int mask = 0; + + for (i = 0; i < value->list.nValue; i++) + { + if (strcasecmp (value->list.value[i].s, "desktop") == 0) + mask |= CompWindowTypeDesktopMask; + else if (strcasecmp (value->list.value[i].s, "dock") == 0) + mask |= CompWindowTypeDockMask; + else if (strcasecmp (value->list.value[i].s, "toolbar") == 0) + mask |= CompWindowTypeToolbarMask; + else if (strcasecmp (value->list.value[i].s, "menu") == 0) + mask |= CompWindowTypeMenuMask; + else if (strcasecmp (value->list.value[i].s, "utility") == 0) + mask |= CompWindowTypeUtilMask; + else if (strcasecmp (value->list.value[i].s, "splash") == 0) + mask |= CompWindowTypeSplashMask; + else if (strcasecmp (value->list.value[i].s, "dialog") == 0) + mask |= CompWindowTypeDialogMask; + else if (strcasecmp (value->list.value[i].s, "modaldialog") == 0) + mask |= CompWindowTypeModalDialogMask; + else if (strcasecmp (value->list.value[i].s, "normal") == 0) + mask |= CompWindowTypeNormalMask; + else if (strcasecmp (value->list.value[i].s, "fullscreen") == 0) + mask |= CompWindowTypeFullscreenMask; + else if (strcasecmp (value->list.value[i].s, "unknown") == 0) + mask |= CompWindowTypeUnknownMask; + } + + return mask; +} diff --git a/src/paint.c b/src/paint.c new file mode 100644 index 00000000..f329d825 --- /dev/null +++ b/src/paint.c @@ -0,0 +1,864 @@ +/* + * Copyright © 2005 Novell, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without + * fee, provided that the above copyright notice appear in all copies + * and that both that copyright notice and this permission notice + * appear in supporting documentation, and that the name of + * Novell, Inc. not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior permission. + * Novell, Inc. makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + * + * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN + * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Author: David Reveman <davidr@novell.com> + */ + +#include <stdlib.h> +#include <string.h> +#include <math.h> + +#include <compiz.h> + +ScreenPaintAttrib defaultScreenPaintAttrib = { + 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -DEFAULT_Z_CAMERA +}; + +WindowPaintAttrib defaultWindowPaintAttrib = { + OPAQUE, 1.0f, 1.0f +}; + +void +preparePaintScreen (CompScreen *screen, + int msSinceLastPaint) {} + +void +donePaintScreen (CompScreen *screen) {} + +void +translateRotateScreen (const ScreenPaintAttrib *sa) +{ + glTranslatef (sa->xTranslate, + sa->yTranslate, + sa->zTranslate + sa->zCamera); + + glRotatef (sa->xRotate, 0.0f, 1.0f, 0.0f); + glRotatef (sa->vRotate, + 1.0f - sa->xRotate / 90.0f, + 0.0f, + sa->xRotate / 90.0f); + glRotatef (sa->yRotate, 0.0f, 1.0f, 0.0f); +} + +void +paintTransformedScreen (CompScreen *screen, + const ScreenPaintAttrib *sAttrib, + unsigned int mask) +{ + CompWindow *w; + int windowMask; + int backgroundMask; + + glPushMatrix (); + + translateRotateScreen (sAttrib); + + glTranslatef (-0.5f, -0.5f, -sAttrib->zTranslate); + glScalef (1.0f / screen->width, -1.0f / screen->height, 1.0f); + glTranslatef (0.0f, -screen->height, 0.0f); + + if (mask & PAINT_SCREEN_TRANSFORMED_MASK) + { + windowMask = PAINT_WINDOW_ON_TRANSFORMED_SCREEN_MASK; + backgroundMask = PAINT_BACKGROUND_ON_TRANSFORMED_SCREEN_MASK; + + if (mask & PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS_MASK) + { + backgroundMask |= PAINT_BACKGROUND_WITH_STENCIL_MASK; + + (*screen->paintBackground) (screen, &screen->region, + backgroundMask); + + glEnable (GL_STENCIL_TEST); + + for (w = screen->windows; w; w = w->next) + { + if (w->destroyed || w->attrib.map_state != IsViewable) + continue; + + if (w->damaged) + (*screen->paintWindow) (w, &w->paint, &screen->region, + windowMask); + } + + glDisable (GL_STENCIL_TEST); + + glPopMatrix (); + + return; + } + } + else + windowMask = backgroundMask = 0; + + (*screen->paintBackground) (screen, &screen->region, backgroundMask); + + for (w = screen->windows; w; w = w->next) + { + if (w->destroyed || w->attrib.map_state != IsViewable) + continue; + + if (w->damaged) + (*screen->paintWindow) (w, &w->paint, &screen->region, windowMask); + } + + glPopMatrix (); +} + +Bool +paintScreen (CompScreen *screen, + const ScreenPaintAttrib *sAttrib, + Region region, + unsigned int mask) +{ + static Region tmpRegion = NULL; + CompWindow *w; + + if (mask & PAINT_SCREEN_REGION_MASK) + { + if (mask & PAINT_SCREEN_TRANSFORMED_MASK) + { + if (mask & PAINT_SCREEN_FULL_MASK) + { + (*screen->paintTransformedScreen) (screen, sAttrib, mask); + + return TRUE; + } + + return FALSE; + } + + /* fall through and redraw region */ + } + else if (mask & PAINT_SCREEN_FULL_MASK) + { + (*screen->paintTransformedScreen) (screen, sAttrib, mask); + + return TRUE; + } + else + return FALSE; + + glPushMatrix (); + + glTranslatef (-0.5f, -0.5f, -DEFAULT_Z_CAMERA); + glScalef (1.0f / screen->width, -1.0f / screen->height, 1.0f); + glTranslatef (0.0f, -screen->height, 0.0f); + + if (mask & PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS_MASK) + { + (*screen->paintBackground) (screen, region, 0); + + /* paint all windows from bottom to top */ + for (w = screen->windows; w; w = w->next) + { + if (w->destroyed || w->attrib.map_state != IsViewable) + continue; + + (*screen->paintWindow) (w, &w->paint, region, 0); + } + } + else + { + if (!tmpRegion) + { + tmpRegion = XCreateRegion (); + if (!tmpRegion) + return FALSE; + } + + XSubtractRegion (region, &emptyRegion, tmpRegion); + + /* paint solid windows */ + for (w = screen->reverseWindows; w; w = w->prev) + { + if (w->destroyed || w->invisible) + continue; + + if (tmpRegion->numRects) + { + if ((*screen->paintWindow) (w, &w->paint, tmpRegion, + PAINT_WINDOW_SOLID_MASK)) + XSubtractRegion (tmpRegion, w->region, tmpRegion); + } + + /* copy region */ + XSubtractRegion (tmpRegion, &emptyRegion, w->clip); + } + + if (tmpRegion->numRects) + (*screen->paintBackground) (screen, tmpRegion, 0); + + /* paint translucent windows */ + for (w = screen->windows; w; w = w->next) + { + if (w->destroyed || w->invisible) + continue; + + if (w->clip->numRects) + (*screen->paintWindow) (w, &w->paint, w->clip, + PAINT_WINDOW_TRANSLUCENT_MASK); + } + } + + glPopMatrix (); + + return TRUE; +} + +#define ADD_RECT(data, m, n, x1, y1, x2, y2) \ + for (it = 0; it < n; it++) \ + { \ + *(data)++ = COMP_TEX_COORD_X (&m[it], x1); \ + *(data)++ = COMP_TEX_COORD_Y (&m[it], y2); \ + } \ + *(data)++ = (x1); \ + *(data)++ = (y2); \ + for (it = 0; it < n; it++) \ + { \ + *(data)++ = COMP_TEX_COORD_X (&m[it], x2); \ + *(data)++ = COMP_TEX_COORD_Y (&m[it], y2); \ + } \ + *(data)++ = (x2); \ + *(data)++ = (y2); \ + for (it = 0; it < n; it++) \ + { \ + *(data)++ = COMP_TEX_COORD_X (&m[it], x2); \ + *(data)++ = COMP_TEX_COORD_Y (&m[it], y1); \ + } \ + *(data)++ = (x2); \ + *(data)++ = (y1); \ + for (it = 0; it < n; it++) \ + { \ + *(data)++ = COMP_TEX_COORD_X (&m[it], x1); \ + *(data)++ = COMP_TEX_COORD_Y (&m[it], y1); \ + } \ + *(data)++ = (x1); \ + *(data)++ = (y1) + +#define ADD_QUAD(data, m, n, x1, y1, x2, y2) \ + for (it = 0; it < n; it++) \ + { \ + *(data)++ = COMP_TEX_COORD_XY (&m[it], x1, y2); \ + *(data)++ = COMP_TEX_COORD_YX (&m[it], x1, y2); \ + } \ + *(data)++ = (x1); \ + *(data)++ = (y2); \ + for (it = 0; it < n; it++) \ + { \ + *(data)++ = COMP_TEX_COORD_XY (&m[it], x2, y2); \ + *(data)++ = COMP_TEX_COORD_YX (&m[it], x2, y2); \ + } \ + *(data)++ = (x2); \ + *(data)++ = (y2); \ + for (it = 0; it < n; it++) \ + { \ + *(data)++ = COMP_TEX_COORD_XY (&m[it], x2, y1); \ + *(data)++ = COMP_TEX_COORD_YX (&m[it], x2, y1); \ + } \ + *(data)++ = (x2); \ + *(data)++ = (y1); \ + for (it = 0; it < n; it++) \ + { \ + *(data)++ = COMP_TEX_COORD_XY (&m[it], x1, y1); \ + *(data)++ = COMP_TEX_COORD_YX (&m[it], x1, y1); \ + } \ + *(data)++ = (x1); \ + *(data)++ = (y1) + + +Bool +moreWindowVertices (CompWindow *w, + int newSize) +{ + if (newSize > w->vertexSize) + { + GLfloat *vertices; + + vertices = realloc (w->vertices, sizeof (GLfloat) * newSize); + if (!vertices) + return FALSE; + + w->vertices = vertices; + w->vertexSize = newSize; + } + + return TRUE; +} + +Bool +moreWindowIndices (CompWindow *w, + int newSize) +{ + if (newSize > w->indexSize) + { + GLushort *indices; + + indices = realloc (w->indices, sizeof (GLushort) * newSize); + if (!indices) + return FALSE; + + w->indices = indices; + w->indexSize = newSize; + } + + return TRUE; +} + +void +addWindowGeometry (CompWindow *w, + CompMatrix *matrix, + int nMatrix, + Region region, + Region clip) +{ + BoxRec full; + + w->texUnits = nMatrix; + + full = clip->extents; + if (region->extents.x1 > full.x1) + full.x1 = region->extents.x1; + if (region->extents.y1 > full.y1) + full.y1 = region->extents.y1; + if (region->extents.x2 < full.x2) + full.x2 = region->extents.x2; + if (region->extents.y2 < full.y2) + full.y2 = region->extents.y2; + + if (full.x1 < full.x2 && full.y1 < full.y2) + { + BoxPtr pBox; + int nBox; + BoxPtr pClip; + int nClip; + BoxRec cbox; + int vSize; + int n, it, x1, y1, x2, y2; + GLfloat *d; + Bool rect = TRUE; + + for (it = 0; it < nMatrix; it++) + { + if (matrix[it].xy != 0.0f && matrix[it].yx != 0.0f) + { + rect = FALSE; + break; + } + } + + pBox = region->rects; + nBox = region->numRects; + + vSize = 2 + nMatrix * 2; + + n = w->vCount / 4; + + if ((n + nBox) * vSize * 4 > w->vertexSize) + { + if (!moreWindowVertices (w, (n + nBox) * vSize * 4)) + return; + } + + d = w->vertices + (w->vCount * vSize); + + while (nBox--) + { + x1 = pBox->x1; + y1 = pBox->y1; + x2 = pBox->x2; + y2 = pBox->y2; + + pBox++; + + if (x1 < full.x1) + x1 = full.x1; + if (y1 < full.y1) + y1 = full.y1; + if (x2 > full.x2) + x2 = full.x2; + if (y2 > full.y2) + y2 = full.y2; + + if (x1 < x2 && y1 < y2) + { + nClip = clip->numRects; + + if (nClip == 1) + { + if (rect) + { + ADD_RECT (d, matrix, nMatrix, x1, y1, x2, y2); + } + else + { + ADD_QUAD (d, matrix, nMatrix, x1, y1, x2, y2); + } + + n++; + } + else + { + pClip = clip->rects; + + if (((n + nClip) * vSize * 4) > w->vertexSize) + { + if (!moreWindowVertices (w, (n + nClip) * vSize * 4)) + return; + + d = w->vertices + (n * vSize * 4); + } + + while (nClip--) + { + cbox = *pClip; + + pClip++; + + if (cbox.x1 < x1) + cbox.x1 = x1; + if (cbox.y1 < y1) + cbox.y1 = y1; + if (cbox.x2 > x2) + cbox.x2 = x2; + if (cbox.y2 > y2) + cbox.y2 = y2; + + if (cbox.x1 < cbox.x2 && cbox.y1 < cbox.y2) + { + if (rect) + { + ADD_RECT (d, matrix, nMatrix, + cbox.x1, cbox.y1, cbox.x2, cbox.y2); + } + else + { + ADD_QUAD (d, matrix, nMatrix, + cbox.x1, cbox.y1, cbox.x2, cbox.y2); + } + + n++; + } + } + } + } + } + w->vCount = n * 4; + } +} + +void +drawWindowGeometry (CompWindow *w) +{ + int texUnit = w->texUnits; + int currentTexUnit = 0; + int stride = (1 + texUnit) * 2; + GLfloat *vertices = w->vertices + (stride - 2); + + stride *= sizeof (GLfloat); + + glVertexPointer (2, GL_FLOAT, stride, vertices); + + while (texUnit--) + { + if (texUnit != currentTexUnit) + { + w->screen->clientActiveTexture (GL_TEXTURE0_ARB + texUnit); + currentTexUnit = texUnit; + } + vertices -= 2; + glTexCoordPointer (2, GL_FLOAT, stride, vertices); + } + + glDrawArrays (GL_QUADS, 0, w->vCount); +} + +void +drawWindowTexture (CompWindow *w, + CompTexture *texture, + const WindowPaintAttrib *attrib, + unsigned int mask) +{ + int filter; + + glPushMatrix (); + + if (mask & PAINT_WINDOW_TRANSFORMED_MASK) + { + glTranslatef (w->attrib.x, w->attrib.y, 0.0f); + glScalef (attrib->xScale, attrib->yScale, 0.0f); + glTranslatef (-w->attrib.x, -w->attrib.y, 0.0f); + + filter = COMP_TEXTURE_FILTER_GOOD; + } + else if (mask & PAINT_WINDOW_ON_TRANSFORMED_SCREEN_MASK) + { + filter = COMP_TEXTURE_FILTER_GOOD; + } + else + { + filter = COMP_TEXTURE_FILTER_FAST; + } + + if (w->screen->canDoSaturated && attrib->saturation != COLOR) + { + GLfloat constant[4]; + + if (mask & PAINT_WINDOW_TRANSLUCENT_MASK) + glEnable (GL_BLEND); + + enableTexture (w->screen, texture, filter); + + glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + + glTexEnvf (GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE); + glTexEnvf (GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE); + glTexEnvf (GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR); + glTexEnvf (GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_PRIMARY_COLOR); + glTexEnvf (GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); + glTexEnvf (GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); + glTexEnvf (GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_ALPHA); + + glTexEnvf (GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE); + glTexEnvf (GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE); + glTexEnvf (GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); + + glColor4f (1.0f, 1.0f, 1.0f, 0.5f); + + w->screen->activeTexture (GL_TEXTURE1_ARB); + + enableTexture (w->screen, texture, filter); + + glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + + glTexEnvf (GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_DOT3_RGB); + glTexEnvf (GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS); + glTexEnvf (GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT); + glTexEnvf (GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); + glTexEnvf (GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); + + if (w->screen->canDoSlightlySaturated && attrib->saturation > 0) + { + glTexEnvf (GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE); + glTexEnvf (GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS); + glTexEnvf (GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); + + constant[0] = 0.5f + 0.5f * RED_SATURATION_WEIGHT; + constant[1] = 0.5f + 0.5f * GREEN_SATURATION_WEIGHT; + constant[2] = 0.5f + 0.5f * BLUE_SATURATION_WEIGHT; + constant[3] = 1.0; + + glTexEnvfv (GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, constant); + + w->screen->activeTexture (GL_TEXTURE2_ARB); + + enableTexture (w->screen, texture, filter); + + glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + + glTexEnvf (GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE); + glTexEnvf (GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE0); + glTexEnvf (GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PREVIOUS); + glTexEnvf (GL_TEXTURE_ENV, GL_SOURCE2_RGB, GL_CONSTANT); + glTexEnvf (GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); + glTexEnvf (GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); + glTexEnvf (GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_ALPHA); + + glTexEnvf (GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE); + glTexEnvf (GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS); + glTexEnvf (GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); + + constant[3] = attrib->saturation / 65535.0f; + + glTexEnvfv (GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, constant); + + if (attrib->opacity < OPAQUE || attrib->brightness != BRIGHT) + { + w->screen->activeTexture (GL_TEXTURE3_ARB); + + enableTexture (w->screen, texture, filter); + + constant[3] = attrib->opacity / 65535.0f; + constant[0] = constant[1] = constant[2] = constant[3] * + attrib->brightness / 65535.0f; + + glTexEnvfv (GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, constant); + + glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + + glTexEnvf (GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); + glTexEnvf (GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS); + glTexEnvf (GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT); + glTexEnvf (GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); + glTexEnvf (GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); + + glTexEnvf (GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE); + glTexEnvf (GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS); + glTexEnvf (GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_CONSTANT); + glTexEnvf (GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); + glTexEnvf (GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA); + + (*w->screen->drawWindowGeometry) (w); + + disableTexture (texture); + + glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + + w->screen->activeTexture (GL_TEXTURE2_ARB); + } + else + { + (*w->screen->drawWindowGeometry) (w); + } + + disableTexture (texture); + + glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + + w->screen->activeTexture (GL_TEXTURE1_ARB); + } + else + { + glTexEnvf (GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE); + glTexEnvf (GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS); + glTexEnvf (GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_CONSTANT); + glTexEnvf (GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); + glTexEnvf (GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA); + + constant[3] = attrib->opacity / 65535.0f; + constant[0] = constant[1] = constant[2] = constant[3] * + attrib->brightness / 65535.0f; + + constant[0] = 0.5f + 0.5f * RED_SATURATION_WEIGHT * constant[0]; + constant[1] = 0.5f + 0.5f * GREEN_SATURATION_WEIGHT * constant[1]; + constant[2] = 0.5f + 0.5f * BLUE_SATURATION_WEIGHT * constant[2]; + + glTexEnvfv (GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, constant); + + (*w->screen->drawWindowGeometry) (w); + } + + disableTexture (texture); + + glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + + w->screen->activeTexture (GL_TEXTURE0_ARB); + + disableTexture (texture); + + glColor4usv (defaultColor); + glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + + if (mask & PAINT_WINDOW_TRANSLUCENT_MASK) + glDisable (GL_BLEND); + } + else + { + enableTexture (w->screen, texture, filter); + + if (mask & PAINT_WINDOW_TRANSLUCENT_MASK) + { + glEnable (GL_BLEND); + if (attrib->opacity != OPAQUE || attrib->brightness != BRIGHT) + { + GLushort color; + + color = (attrib->opacity * attrib->brightness) >> 16; + + glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glColor4us (color, color, color, attrib->opacity); + + (*w->screen->drawWindowGeometry) (w); + + glColor4usv (defaultColor); + glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + } + else + { + (*w->screen->drawWindowGeometry) (w); + } + + glDisable (GL_BLEND); + } + else if (attrib->brightness != BRIGHT) + { + glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glColor4us (attrib->brightness, attrib->brightness, + attrib->brightness, BRIGHT); + + (*w->screen->drawWindowGeometry) (w); + + glColor4usv (defaultColor); + glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + } + else + { + (*w->screen->drawWindowGeometry) (w); + } + + disableTexture (texture); + } + + glPopMatrix (); +} + +Bool +paintWindow (CompWindow *w, + const WindowPaintAttrib *attrib, + Region region, + unsigned int mask) +{ + if (mask & PAINT_WINDOW_SOLID_MASK) + { + if (w->alpha) + return FALSE; + + if (attrib->opacity != OPAQUE) + return FALSE; + } + else if (mask & PAINT_WINDOW_TRANSLUCENT_MASK) + { + if (!w->alpha && attrib->opacity == OPAQUE) + return FALSE; + } + else + { + if (w->alpha || attrib->opacity != OPAQUE) + mask |= PAINT_WINDOW_TRANSLUCENT_MASK; + else + mask |= PAINT_WINDOW_SOLID_MASK; + } + + if (!w->texture.pixmap) + bindWindow (w); + + if (mask & PAINT_WINDOW_TRANSFORMED_MASK) + region = &infiniteRegion; + + w->vCount = 0; + (*w->screen->addWindowGeometry) (w, &w->matrix, 1, w->region, region); + if (w->vCount) + drawWindowTexture (w, &w->texture, attrib, mask); + + return TRUE; +} + +void +paintBackground (CompScreen *s, + Region region, + unsigned int mask) +{ + CompTexture *bg = &s->backgroundTexture; + BoxPtr pBox = region->rects; + int n, nBox = region->numRects; + GLfloat *d, *data; + + if (!nBox) + return; + + if (s->desktopWindowCount) + { + if (bg->name) + { + finiTexture (s, bg); + initTexture (s, bg); + } + + if (!(mask & PAINT_BACKGROUND_WITH_STENCIL_MASK)) + return; + } + else + { + if (!bg->name) + updateScreenBackground (s, bg); + } + + data = malloc (sizeof (GLfloat) * nBox * 16); + if (!data) + return; + + d = data; + n = nBox; + while (n--) + { + *d++ = COMP_TEX_COORD_X (&bg->matrix, pBox->x1); + *d++ = COMP_TEX_COORD_Y (&bg->matrix, pBox->y2); + + *d++ = pBox->x1; + *d++ = pBox->y2; + + *d++ = COMP_TEX_COORD_X (&bg->matrix, pBox->x2); + *d++ = COMP_TEX_COORD_Y (&bg->matrix, pBox->y2); + + *d++ = pBox->x2; + *d++ = pBox->y2; + + *d++ = COMP_TEX_COORD_X (&bg->matrix, pBox->x2); + *d++ = COMP_TEX_COORD_Y (&bg->matrix, pBox->y1); + + *d++ = pBox->x2; + *d++ = pBox->y1; + + *d++ = COMP_TEX_COORD_X (&bg->matrix, pBox->x1); + *d++ = COMP_TEX_COORD_Y (&bg->matrix, pBox->y1); + + *d++ = pBox->x1; + *d++ = pBox->y1; + + pBox++; + } + + if (mask & PAINT_BACKGROUND_WITH_STENCIL_MASK) + { + glEnable (GL_STENCIL_TEST); + glStencilFunc (GL_ALWAYS, s->stencilRef, ~0); + glStencilOp (GL_REPLACE, GL_REPLACE, GL_REPLACE); + } + + glTexCoordPointer (2, GL_FLOAT, sizeof (GLfloat) * 4, data); + glVertexPointer (2, GL_FLOAT, sizeof (GLfloat) * 4, data + 2); + + if (s->desktopWindowCount) + { + glDrawArrays (GL_QUADS, 0, nBox * 4); + } + else + { + if (mask & PAINT_BACKGROUND_ON_TRANSFORMED_SCREEN_MASK) + enableTexture (s, bg, COMP_TEXTURE_FILTER_GOOD); + else + enableTexture (s, bg, COMP_TEXTURE_FILTER_FAST); + + glDrawArrays (GL_QUADS, 0, nBox * 4); + + disableTexture (bg); + } + + if (mask & PAINT_BACKGROUND_WITH_STENCIL_MASK) + { + glStencilFunc (GL_EQUAL, s->stencilRef, ~0); + glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP); + glDisable (GL_STENCIL_TEST); + } + + free (data); +} diff --git a/src/plugin.c b/src/plugin.c new file mode 100644 index 00000000..fb9ddfc3 --- /dev/null +++ b/src/plugin.c @@ -0,0 +1,404 @@ +/* + * Copyright © 2005 Novell, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without + * fee, provided that the above copyright notice appear in all copies + * and that both that copyright notice and this permission notice + * appear in supporting documentation, and that the name of + * Novell, Inc. not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior permission. + * Novell, Inc. makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + * + * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN + * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Author: David Reveman <davidr@novell.com> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <dlfcn.h> + +#include <compiz.h> + +#define HOME_PLUGINDIR ".compiz/plugins" + +CompPlugin *plugins = 0; + +static Bool +initPlugin (CompPlugin *p) +{ + CompDisplay *d = compDisplays; + int failed = 0; + + if (!(*p->vTable->init) (p)) + { + fprintf (stderr, "%s: InitPlugin '%s' failed\n", programName, + p->vTable->name); + return FALSE; + } + + if (d) + { + if ((*d->initPluginForDisplay) (p, d)) + { + CompScreen *s, *failedScreen = d->screens; + + for (s = d->screens; s; s = s->next) + { + if (!p->vTable->initScreen || (*s->initPluginForScreen) (p, s)) + { + CompWindow *w, *failedWindow = s->windows; + + for (w = s->windows; w; w = w->next) + { + if (p->vTable->initWindow && + !(*p->vTable->initWindow) (p, w)) + { + fprintf (stderr, "%s: Plugin '%s':initWindow " + "failed\n", programName, p->vTable->name); + failedWindow = w; + failed = 1; + break; + } + } + + for (w = s->windows; w != failedWindow; w = w->next) + { + if (p->vTable->finiWindow) + (*p->vTable->finiWindow) (p, w); + } + } + else + { + fprintf (stderr, "%s: Plugin '%s':initScreen failed\n", + programName, p->vTable->name); + failedScreen = s; + failed = 1; + break; + } + } + + for (s = d->screens; s != failedScreen; s = s->next) + (*s->finiPluginForScreen) (p, s); + } + else + { + fprintf (stderr, "%s: Plugin '%s':initDisplay failed\n", + programName, p->vTable->name); + + failed = 1; + (*d->finiPluginForDisplay) (p, d); + } + } + + if (failed) + { + (*p->vTable->fini) (p); + + return FALSE; + } + + return TRUE; +} + +static void +finiPlugin (CompPlugin *p) +{ + CompDisplay *d = compDisplays; + CompScreen *s; + + if (d) + { + for (s = d->screens; s; s = s->next) + { + CompWindow *w = s->windows; + + if (p->vTable->finiWindow) + { + for (w = s->windows; w; w = w->next) + (*p->vTable->finiWindow) (p, w); + } + + (*s->finiPluginForScreen) (p, s); + } + + (*d->finiPluginForDisplay) (p, d); + } + + (*p->vTable->fini) (p); +} + +void +screenInitPlugins (CompScreen *s) +{ + CompPlugin *p; + int i, j = 0; + + for (p = plugins; p; p = p->next) + j++; + + while (j--) + { + i = 0; + for (p = plugins; i < j; p = p->next) + i++; + + if (p->vTable->initScreen) + (*s->initPluginForScreen) (p, s); + } +} + +void +screenFiniPlugins (CompScreen *s) +{ + CompPlugin *p; + + for (p = plugins; p; p = p->next) + { + if (p->vTable->finiScreen) + (*s->initPluginForScreen) (p, s); + } +} + +void +windowInitPlugins (CompWindow *w) +{ + CompPlugin *p; + + for (p = plugins; p; p = p->next) + { + if (p->vTable->initWindow) + (*p->vTable->initWindow) (p, w); + } +} + +void +windowFiniPlugins (CompWindow *w) +{ + CompPlugin *p; + + for (p = plugins; p; p = p->next) + { + if (p->vTable->finiWindow) + (*p->vTable->finiWindow) (p, w); + } +} + +CompPlugin * +findActivePlugin (char *name) +{ + CompPlugin *p; + + for (p = plugins; p; p = p->next) + { + if (strcmp (p->vTable->name, name) == 0) + return p; + } + + return 0; +} + +CompPlugin * +loadPlugin (char *name) +{ + CompPlugin *p; + char *file, *home, *plugindir; + + p = malloc (sizeof (CompPlugin)); + if (!p) + return 0; + + file = malloc (strlen (name) + 7); + if (!file) + { + free (p); + return 0; + } + + sprintf (file, "lib%s.so", name); + + p->next = 0; + p->dlhand = 0; + p->vTable = 0; + + home = getenv ("HOME"); + if (home) + { + plugindir = malloc (strlen (home) + + strlen (HOME_PLUGINDIR) + + strlen (file) + 3); + if (plugindir) + { + sprintf (plugindir, "%s/%s/%s", home, HOME_PLUGINDIR, file); + p->dlhand = dlopen (plugindir, RTLD_LAZY); + free (plugindir); + } + } + + if (!p->dlhand) + { + plugindir = malloc (strlen (PLUGINDIR) + strlen (file) + 2); + if (plugindir) + { + sprintf (plugindir, "%s/%s", PLUGINDIR, file); + p->dlhand = dlopen (plugindir, RTLD_LAZY); + free (plugindir); + } + + if (!p->dlhand) + p->dlhand = dlopen (file, RTLD_LAZY); + } + + if (p->dlhand) + { + PluginGetInfoProc getInfo; + char *error; + + dlerror (); + + getInfo = (PluginGetInfoProc) dlsym (p->dlhand, "getCompPluginInfo"); + + error = dlerror (); + if (error) + { + fprintf (stderr, "%s: dlsym: %s\n", programName, error); + + getInfo = 0; + } + + if (getInfo) + { + p->vTable = (*getInfo) (); + if (!p->vTable) + { + fprintf (stderr, "%s: Couldn't get vtable from '%s' plugin\n", + programName, file); + + dlclose (p->dlhand); + free (p); + p = 0; + } + } + else + { + fprintf (stderr, "%s: Failed to lookup getCompPluginInfo in '%s' " + "plugin\n", programName, file); + + dlclose (p->dlhand); + free (p); + p = 0; + } + } + else + { + fprintf (stderr, "%s: Couldn't load plugin '%s'\n", programName, + file); + free (p); + p = 0; + } + + free (file); + + return p; +} + +void +unloadPlugin (CompPlugin *p) +{ + dlclose (p->dlhand); + free (p); +} + +static Bool +checkPluginDeps (CompPlugin *p) +{ + CompPluginDep *deps = p->vTable->deps; + int nDeps = p->vTable->nDeps; + + while (nDeps--) + { + switch (deps->rule) { + case CompPluginRuleBefore: + if (findActivePlugin (deps->plugin)) + { + fprintf (stderr, "%s: '%s' plugin must be loaded before '%s' " + "plugin\n", programName, p->vTable->name, deps->plugin); + + return FALSE; + } + break; + case CompPluginRuleAfter: + if (!findActivePlugin (deps->plugin)) + { + fprintf (stderr, "%s: '%s' plugin must be loaded after '%s' " + "plugin\n", programName, p->vTable->name, deps->plugin); + + return FALSE; + } + break; + } + + deps++; + } + + return TRUE; +} + +Bool +pushPlugin (CompPlugin *p) +{ + if (findActivePlugin (p->vTable->name)) + { + fprintf (stderr, "%s: Plugin '%s' already active\n", programName, + p->vTable->name); + + return FALSE; + } + + if (!checkPluginDeps (p)) + { + fprintf (stderr, "%s: Can't activate '%s' plugin due to dependency " + "problems\n", programName, p->vTable->name); + + return FALSE; + } + + p->next = plugins; + plugins = p; + + if (!initPlugin (p)) + { + fprintf (stderr, "%s: Couldn't activate plugin '%s'\n", programName, + p->vTable->name); + plugins = p->next; + + return FALSE; + } + + return TRUE; +} + +CompPlugin * +popPlugin (void) +{ + CompPlugin *p = plugins; + + if (!p) + return 0; + + finiPlugin (p); + + plugins = p->next; + + return p; +} diff --git a/src/privates.c b/src/privates.c new file mode 100644 index 00000000..ea5b57ac --- /dev/null +++ b/src/privates.c @@ -0,0 +1,68 @@ +/* + * Copyright © 2005 Novell, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without + * fee, provided that the above copyright notice appear in all copies + * and that both that copyright notice and this permission notice + * appear in supporting documentation, and that the name of + * Novell, Inc. not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior permission. + * Novell, Inc. makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + * + * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN + * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Author: David Reveman <davidr@novell.com> + */ + +#include <stdlib.h> + +#include <compiz.h> + +int +allocatePrivateIndex (int *len, + char **indices, + ReallocPrivatesProc reallocProc, + void *closure) +{ + char *newIndices; + int i; + + for (i = 0; i < *len; i++) + { + if (!(*indices)[i]) + { + (*indices)[i] = 1; + return i; + } + } + + newIndices = (char *) realloc (*indices, (*len + 1) * sizeof (char)); + if (!newIndices) + return -1; + + newIndices[*len] = 1; + *indices = newIndices; + + if (!(*reallocProc) (*len + 1, closure)) + return -1; + + return (*len)++; +} + +void +freePrivateIndex (int len, + char *indices, + int index) +{ + if (index < len) + indices[index] = 0; +} diff --git a/src/readpng.c b/src/readpng.c new file mode 100644 index 00000000..79bb5a53 --- /dev/null +++ b/src/readpng.c @@ -0,0 +1,266 @@ +/* + * Copyright © 2005 Novell, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without + * fee, provided that the above copyright notice appear in all copies + * and that both that copyright notice and this permission notice + * appear in supporting documentation, and that the name of + * Novell, Inc. not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior permission. + * Novell, Inc. makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + * + * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN + * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Author: David Reveman <davidr@novell.com> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <png.h> +#include <setjmp.h> + +#include <compiz.h> + +#define HOME_IMAGEDIR ".compiz/images" + +#define PNG_SIG_SIZE 8 + +static void +premultiplyData (png_structp png, + png_row_infop row_info, + png_bytep data) +{ + unsigned int i; + + for (i = 0; i < row_info->rowbytes; i += 4) + { + unsigned char *base = &data[i]; + unsigned char blue = base[0]; + unsigned char green = base[1]; + unsigned char red = base[2]; + unsigned char alpha = base[3]; + int p; + + red = (unsigned) red * (unsigned) alpha / 255; + green = (unsigned) green * (unsigned) alpha / 255; + blue = (unsigned) blue * (unsigned) alpha / 255; + + p = (alpha << 24) | (red << 16) | (green << 8) | (blue << 0); + memcpy (base, &p, sizeof (int)); + } +} + +static Bool +readPngData (png_struct *png, + png_info *info, + char **data, + unsigned int *width, + unsigned int *height) +{ + png_uint_32 png_width, png_height; + int depth, color_type, interlace, i; + unsigned int pixel_size; + png_byte **row_pointers; + + png_read_info (png, info); + + png_get_IHDR (png, info, + &png_width, &png_height, &depth, + &color_type, &interlace, NULL, NULL); + + *width = png_width; + *height = png_height; + + /* convert palette/gray image to rgb */ + if (color_type == PNG_COLOR_TYPE_PALETTE) + png_set_palette_to_rgb (png); + + /* expand gray bit depth if needed */ + if (color_type == PNG_COLOR_TYPE_GRAY && depth < 8) + png_set_gray_1_2_4_to_8 (png); + + /* transform transparency to alpha */ + if (png_get_valid(png, info, PNG_INFO_tRNS)) + png_set_tRNS_to_alpha (png); + + if (depth == 16) + png_set_strip_16 (png); + + if (depth < 8) + png_set_packing (png); + + /* convert grayscale to RGB */ + if (color_type == PNG_COLOR_TYPE_GRAY || + color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_gray_to_rgb (png); + + if (interlace != PNG_INTERLACE_NONE) + png_set_interlace_handling (png); + + png_set_bgr (png); + png_set_filler (png, 0xff, PNG_FILLER_AFTER); + + png_set_read_user_transform_fn (png, premultiplyData); + + png_read_update_info (png, info); + + pixel_size = 4; + *data = (char *) malloc (png_width * png_height * pixel_size); + if (*data == NULL) + return FALSE; + + row_pointers = (png_byte **) malloc (png_height * sizeof (char *)); + if (!row_pointers) + { + free (*data); + return FALSE; + } + + for (i = 0; i < png_height; i++) + row_pointers[i] = (png_byte *) (*data + i * png_width * pixel_size); + + png_read_image (png, row_pointers); + png_read_end (png, info); + + free (row_pointers); + + return TRUE; +} + +Bool +readPng (const char *filename, + char **data, + unsigned int *width, + unsigned int *height) +{ + unsigned char png_sig[PNG_SIG_SIZE]; + FILE *file; + int sig_bytes; + png_struct *png; + png_info *info; + Bool status; + + file = fopen (filename, "r"); + if (!file) + { + char *home, *imagedir; + + home = getenv ("HOME"); + if (home) + { + imagedir = malloc (strlen (home) + + strlen (HOME_IMAGEDIR) + + strlen (filename) + 3); + if (imagedir) + { + sprintf (imagedir, "%s/%s/%s", home, HOME_IMAGEDIR, filename); + file = fopen (imagedir, "r"); + free (imagedir); + } + } + + if (!file) + { + imagedir = malloc (strlen (IMAGEDIR) + strlen (filename) + 2); + if (imagedir) + { + sprintf (imagedir, "%s/%s", IMAGEDIR, filename); + file = fopen (imagedir, "r"); + free (imagedir); + } + + if (!file) + return FALSE; + } + } + + sig_bytes = fread (png_sig, 1, PNG_SIG_SIZE, file); + if (png_check_sig (png_sig, sig_bytes) == 0) + { + fclose (file); + return FALSE; + } + + png = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png) + return FALSE; + + info = png_create_info_struct (png); + if (info == NULL) + { + png_destroy_read_struct (&png, NULL, NULL); + fclose (file); + + return FALSE; + } + + png_init_io (png, file); + png_set_sig_bytes (png, sig_bytes); + + status = readPngData (png, info, data, width, height); + + png_destroy_read_struct (&png, &info, NULL); + fclose (file); + + return status; +} + +static void +userReadData (png_structp png_ptr, + png_bytep data, + png_size_t length) +{ + const unsigned char **buffer = (const unsigned char **) + png_get_io_ptr (png_ptr); + + memcpy (data, *buffer, length); + *buffer += length; +} + +Bool +readPngBuffer (const unsigned char *buffer, + char **data, + unsigned int *width, + unsigned int *height) +{ + unsigned char png_sig[PNG_SIG_SIZE]; + png_struct *png; + png_info *info; + const unsigned char *b = buffer + PNG_SIG_SIZE; + Bool status; + + memcpy (png_sig, buffer, PNG_SIG_SIZE); + if (png_check_sig (png_sig, PNG_SIG_SIZE) == 0) + return FALSE; + + png = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png) + return FALSE; + + info = png_create_info_struct (png); + if (info == NULL) + { + png_destroy_read_struct (&png, NULL, NULL); + return FALSE; + } + + png_set_read_fn (png, (void *) &b, userReadData); + png_set_sig_bytes (png, PNG_SIG_SIZE); + + status = readPngData (png, info, data, width, height); + + png_destroy_read_struct (&png, &info, NULL); + + return status; +} + diff --git a/src/screen.c b/src/screen.c new file mode 100644 index 00000000..27c7d9d8 --- /dev/null +++ b/src/screen.c @@ -0,0 +1,2521 @@ +/* + * Copyright © 2005 Novell, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without + * fee, provided that the above copyright notice appear in all copies + * and that both that copyright notice and this permission notice + * appear in supporting documentation, and that the name of + * Novell, Inc. not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior permission. + * Novell, Inc. makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + * + * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN + * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Author: David Reveman <davidr@novell.com> + */ + +#ifdef HAVE_CONFIG_H +# include "../config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <dlfcn.h> +#include <string.h> +#include <sys/types.h> +#include <unistd.h> + +#include <X11/Xlib.h> +#include <X11/Xatom.h> +#include <X11/extensions/Xrandr.h> +#include <X11/cursorfont.h> + +#include <compiz.h> + +#define DETECT_REFRESH_RATE_DEFAULT TRUE + +#define SCREEN_SIZE_DEFAULT 4 +#define SCREEN_SIZE_MIN 4 +#define SCREEN_SIZE_MAX 32 + +#define CLOSE_WINDOW_KEY_DEFAULT "F4" +#define CLOSE_WINDOW_MODIFIERS_DEFAULT (CompPressMask | CompAltMask) + +#define MAIN_MENU_KEY_DEFAULT "F1" +#define MAIN_MENU_MODIFIERS_DEFAULT (CompPressMask | CompAltMask) + +#define RUN_DIALOG_KEY_DEFAULT "F2" +#define RUN_DIALOG_MODIFIERS_DEFAULT (CompPressMask | CompAltMask) + +#define NUM_OPTIONS(s) (sizeof ((s)->opt) / sizeof (CompOption)) + +static int +reallocScreenPrivate (int size, + void *closure) +{ + CompDisplay *d = (CompDisplay *) closure; + CompScreen *s; + void *privates; + + for (s = d->screens; s; s = s->next) + { + privates = realloc (s->privates, size * sizeof (CompPrivate)); + if (!privates) + return FALSE; + + s->privates = (CompPrivate *) privates; + } + + return TRUE; +} + +int +allocateScreenPrivateIndex (CompDisplay *display) +{ + return allocatePrivateIndex (&display->screenPrivateLen, + &display->screenPrivateIndices, + reallocScreenPrivate, + (void *) display); +} + +void +freeScreenPrivateIndex (CompDisplay *display, + int index) +{ + freePrivateIndex (display->screenPrivateLen, + display->screenPrivateIndices, + index); +} + +static void +setVirtualScreenSize (CompScreen *screen, + int size) +{ + unsigned long data[2]; + + data[0] = screen->width * size; + data[1] = screen->height; + + XChangeProperty (screen->display->display, screen->root, + screen->display->desktopGeometryAtom, + XA_CARDINAL, 32, PropModeReplace, + (unsigned char *) data, 2); + + screen->size = size; +} + +CompOption * +compGetScreenOptions (CompScreen *screen, + int *count) +{ + *count = NUM_OPTIONS (screen); + return screen->opt; +} + +static Bool +setScreenOption (CompScreen *screen, + char *name, + CompOptionValue *value) +{ + CompOption *o; + int index; + + o = compFindOption (screen->opt, NUM_OPTIONS (screen), name, &index); + if (!o) + return FALSE; + + switch (index) { + case COMP_SCREEN_OPTION_DETECT_REFRESH_RATE: + if (compSetBoolOption (o, value)) + return TRUE; + break; + case COMP_SCREEN_OPTION_REFRESH_RATE: + if (screen->opt[COMP_SCREEN_OPTION_DETECT_REFRESH_RATE].value.b) + return FALSE; + + if (compSetIntOption (o, value)) + { + screen->redrawTime = 1000 / o->value.i; + screen->optimalRedrawTime = screen->redrawTime; + return TRUE; + } + break; + case COMP_SCREEN_OPTION_SIZE: + if (compSetIntOption (o, value)) + { + if (o->value.i * screen->width > MAXSHORT) + return FALSE; + + setVirtualScreenSize (screen, o->value.i); + return TRUE; + } + break; + case COMP_SCREEN_OPTION_COMMAND0: + case COMP_SCREEN_OPTION_COMMAND1: + if (compSetStringOption (o, value)) + return TRUE; + break; + case COMP_SCREEN_OPTION_CLOSE_WINDOW: + case COMP_SCREEN_OPTION_MAIN_MENU: + case COMP_SCREEN_OPTION_RUN_DIALOG: + case COMP_SCREEN_OPTION_RUN_COMMAND0: + case COMP_SCREEN_OPTION_RUN_COMMAND1: + if (addScreenBinding (screen, &value->bind)) + { + removeScreenBinding (screen, &o->value.bind); + + if (compSetBindingOption (o, value)) + return TRUE; + } + default: + break; + } + + return FALSE; +} + +static Bool +setScreenOptionForPlugin (CompScreen *screen, + char *plugin, + char *name, + CompOptionValue *value) +{ + CompPlugin *p; + + p = findActivePlugin (plugin); + if (p && p->vTable->setScreenOption) + return (*p->vTable->setScreenOption) (screen, name, value); + + return FALSE; +} + +static void +compScreenInitOptions (CompScreen *screen) +{ + CompOption *o; + + o = &screen->opt[COMP_SCREEN_OPTION_DETECT_REFRESH_RATE]; + o->name = "detect_refresh_rate"; + o->shortDesc = "Detect Refresh Rate"; + o->longDesc = "Automatic detection of refresh rate"; + o->type = CompOptionTypeBool; + o->value.b = DETECT_REFRESH_RATE_DEFAULT; + + o = &screen->opt[COMP_SCREEN_OPTION_REFRESH_RATE]; + o->name = "refresh_rate"; + o->shortDesc = "Refresh Rate"; + o->longDesc = "The rate at which the screen is redrawn (times/second)"; + o->type = CompOptionTypeInt; + o->value.i = defaultRefreshRate; + o->rest.i.min = 1; + o->rest.i.max = 200; + + o = &screen->opt[COMP_SCREEN_OPTION_SIZE]; + o->name = "size"; + o->shortDesc = "Virtual Size"; + o->longDesc = "Screen size multiplier for virtual size"; + o->type = CompOptionTypeInt; + o->value.i = SCREEN_SIZE_DEFAULT; + o->rest.i.min = SCREEN_SIZE_MIN; + o->rest.i.max = SCREEN_SIZE_MAX; + + o = &screen->opt[COMP_SCREEN_OPTION_CLOSE_WINDOW]; + o->name = "close_window"; + o->shortDesc = "Close Window"; + o->longDesc = "Close active window"; + o->type = CompOptionTypeBinding; + o->value.bind.type = CompBindingTypeKey; + o->value.bind.u.key.modifiers = CLOSE_WINDOW_MODIFIERS_DEFAULT; + o->value.bind.u.key.keycode = + XKeysymToKeycode (screen->display->display, + XStringToKeysym (CLOSE_WINDOW_KEY_DEFAULT)); + + o = &screen->opt[COMP_SCREEN_OPTION_MAIN_MENU]; + o->name = "main_menu"; + o->shortDesc = "Main Menu"; + o->longDesc = "Open main menu"; + o->type = CompOptionTypeBinding; + o->value.bind.type = CompBindingTypeKey; + o->value.bind.u.key.modifiers = MAIN_MENU_MODIFIERS_DEFAULT; + o->value.bind.u.key.keycode = + XKeysymToKeycode (screen->display->display, + XStringToKeysym (MAIN_MENU_KEY_DEFAULT)); + + o = &screen->opt[COMP_SCREEN_OPTION_RUN_DIALOG]; + o->name = "run"; + o->shortDesc = "Run"; + o->longDesc = "Run application"; + o->type = CompOptionTypeBinding; + o->value.bind.type = CompBindingTypeKey; + o->value.bind.u.key.modifiers = RUN_DIALOG_MODIFIERS_DEFAULT; + o->value.bind.u.key.keycode = + XKeysymToKeycode (screen->display->display, + XStringToKeysym (RUN_DIALOG_KEY_DEFAULT)); + + o = &screen->opt[COMP_SCREEN_OPTION_COMMAND0]; + o->name = "command0"; + o->shortDesc = "Command line"; + o->longDesc = "Command line to be executed in shell"; + o->type = CompOptionTypeString; + o->value.s = strdup (""); + o->rest.s.string = NULL; + o->rest.s.nString = 0; + + o = &screen->opt[COMP_SCREEN_OPTION_RUN_COMMAND0]; + o->name = "run_command0"; + o->shortDesc = "Run command"; + o->longDesc = "Run shell command"; + o->type = CompOptionTypeBinding; + o->value.bind.type = CompBindingTypeKey; + o->value.bind.u.key.modifiers = 0; + o->value.bind.u.key.keycode = 0; + + o = &screen->opt[COMP_SCREEN_OPTION_COMMAND1]; + o->name = "command1"; + o->shortDesc = "Command line"; + o->longDesc = "Command line to be executed in shell"; + o->type = CompOptionTypeString; + o->value.s = strdup (""); + o->rest.s.string = NULL; + o->rest.s.nString = 0; + + o = &screen->opt[COMP_SCREEN_OPTION_RUN_COMMAND1]; + o->name = "run_command1"; + o->shortDesc = "Run command"; + o->longDesc = "Run shell command"; + o->type = CompOptionTypeBinding; + o->value.bind.type = CompBindingTypeKey; + o->value.bind.u.key.modifiers = 0; + o->value.bind.u.key.keycode = 0; +} + +static Bool +initPluginForScreen (CompPlugin *p, + CompScreen *s) +{ + if (p->vTable->initScreen) + return (*p->vTable->initScreen) (p, s); + + return FALSE; +} + +static void +finiPluginForScreen (CompPlugin *p, + CompScreen *s) +{ + if (p->vTable->finiScreen) + (*p->vTable->finiScreen) (p, s); +} + +static void +updateStartupFeedback (CompScreen *s) +{ + if (s->startupSequences) + XDefineCursor (s->display->display, s->root, s->busyCursor); + else + XDefineCursor (s->display->display, s->root, s->normalCursor); +} + +#define STARTUP_TIMEOUT_DELAY 15000 + +static Bool +startupSequenceTimeout (void *data) +{ + CompScreen *screen = data; + CompStartupSequence *s; + struct timeval now, active; + double elapsed; + + gettimeofday (&now, NULL); + + for (s = screen->startupSequences; s; s = s->next) + { + sn_startup_sequence_get_last_active_time (s->sequence, + &active.tv_sec, + &active.tv_usec); + + elapsed = ((((double) now.tv_sec - active.tv_sec) * 1000000.0 + + (now.tv_usec - active.tv_usec))) / 1000.0; + + if (elapsed > STARTUP_TIMEOUT_DELAY) + sn_startup_sequence_complete (s->sequence); + } + + return TRUE; +} + +static void +addSequence (CompScreen *screen, + SnStartupSequence *sequence) +{ + CompStartupSequence *s; + + s = malloc (sizeof (CompStartupSequence)); + if (!s) + return; + + sn_startup_sequence_ref (sequence); + + s->next = screen->startupSequences; + s->sequence = sequence; + + screen->startupSequences = s; + + if (!screen->startupSequenceTimeoutHandle) + compAddTimeout (1000, + startupSequenceTimeout, + screen); + + updateStartupFeedback (screen); +} + +static void +removeSequence (CompScreen *screen, + SnStartupSequence *sequence) +{ + CompStartupSequence *s, *p = NULL; + + for (s = screen->startupSequences; s; s = s->next) + { + if (s->sequence == sequence) + break; + + p = s; + } + + if (!s) + return; + + sn_startup_sequence_unref (sequence); + + if (p) + p->next = s->next; + else + screen->startupSequences = NULL; + + free (s); + + if (!screen->startupSequences && screen->startupSequenceTimeoutHandle) + { + compRemoveTimeout (screen->startupSequenceTimeoutHandle); + screen->startupSequenceTimeoutHandle = 0; + } + + updateStartupFeedback (screen); +} + +static void +compScreenSnEvent (SnMonitorEvent *event, + void *userData) +{ + CompScreen *screen = userData; + SnStartupSequence *sequence; + + sequence = sn_monitor_event_get_startup_sequence (event); + + switch (sn_monitor_event_get_type (event)) { + case SN_MONITOR_EVENT_INITIATED: + addSequence (screen, sequence); + break; + case SN_MONITOR_EVENT_COMPLETED: + removeSequence (screen, sn_monitor_event_get_startup_sequence (event)); + break; + case SN_MONITOR_EVENT_CHANGED: + case SN_MONITOR_EVENT_CANCELED: + break; + } +} + +static void +frustum (GLfloat left, + GLfloat right, + GLfloat bottom, + GLfloat top, + GLfloat nearval, + GLfloat farval) +{ + GLfloat x, y, a, b, c, d; + GLfloat m[16]; + + x = (2.0 * nearval) / (right - left); + y = (2.0 * nearval) / (top - bottom); + a = (right + left) / (right - left); + b = (top + bottom) / (top - bottom); + c = -(farval + nearval) / ( farval - nearval); + d = -(2.0 * farval * nearval) / (farval - nearval); + +#define M(row,col) m[col*4+row] + M(0,0) = x; M(0,1) = 0.0F; M(0,2) = a; M(0,3) = 0.0F; + M(1,0) = 0.0F; M(1,1) = y; M(1,2) = b; M(1,3) = 0.0F; + M(2,0) = 0.0F; M(2,1) = 0.0F; M(2,2) = c; M(2,3) = d; + M(3,0) = 0.0F; M(3,1) = 0.0F; M(3,2) = -1.0F; M(3,3) = 0.0F; +#undef M + + glMultMatrixf (m); +} + +static void +perspective (GLfloat fovy, + GLfloat aspect, + GLfloat zNear, + GLfloat zFar) +{ + GLfloat xmin, xmax, ymin, ymax; + + ymax = zNear * tan (fovy * M_PI / 360.0); + ymin = -ymax; + xmin = ymin * aspect; + xmax = ymax * aspect; + + frustum (xmin, xmax, ymin, ymax, zNear, zFar); +} + +static void +reshape (CompScreen *s, + int w, + int h) +{ + s->width = w; + s->height = h; + + glMatrixMode (GL_PROJECTION); + glLoadIdentity (); + glMatrixMode (GL_MODELVIEW); + glLoadIdentity (); + glDepthRange (0, 1); + glViewport (-1, -1, 2, 2); + glRasterPos2f (0, 0); + + s->rasterX = s->rasterY = 0; + + glViewport (0, 0, w, h); + glMatrixMode (GL_PROJECTION); + glLoadIdentity (); + perspective (60.0f, 1.0f, 0.1f, 100.0f); + glMatrixMode (GL_MODELVIEW); + + s->region.rects = &s->region.extents; + s->region.numRects = 1; + s->region.extents.x1 = 0; + s->region.extents.y1 = 0; + s->region.extents.x2 = w; + s->region.extents.y2 = h; + s->region.size = 1; + + updateWorkareaForScreen (s); +} + +void +configureScreen (CompScreen *s, + XConfigureEvent *ce) +{ + if (s->attrib.width != ce->width || + s->attrib.height != ce->height) + { + s->attrib.width = ce->width; + s->attrib.height = ce->height; + + reshape (s, ce->width, ce->height); + + damageScreen (s); + } +} + +static FuncPtr +getProcAddress (CompScreen *s, + const char *name) +{ + static void *dlhand = NULL; + FuncPtr funcPtr = NULL; + + if (s->getProcAddress) + funcPtr = s->getProcAddress ((GLubyte *) name); + + if (!funcPtr) + { + if (!dlhand) + dlhand = dlopen (NULL, RTLD_LAZY); + + if (dlhand) + { + dlerror (); + funcPtr = (FuncPtr) dlsym (dlhand, name); + if (dlerror () != NULL) + funcPtr = NULL; + } + } + + return funcPtr; +} + +void +updateScreenBackground (CompScreen *screen, + CompTexture *texture) +{ + Display *dpy = screen->display->display; + Atom pixmapAtom, actualType; + int actualFormat, i, status; + unsigned int width = 1, height = 1, depth = 0; + unsigned long nItems; + unsigned long bytesAfter; + unsigned char *prop; + Pixmap pixmap = 0; + + pixmapAtom = XInternAtom (dpy, "PIXMAP", FALSE); + + for (i = 0; pixmap == 0 && i < 2; i++) + { + status = XGetWindowProperty (dpy, screen->root, + screen->display->xBackgroundAtom[i], + 0, 4, FALSE, AnyPropertyType, + &actualType, &actualFormat, &nItems, + &bytesAfter, &prop); + + if (status == Success && nItems && prop) + { + if (actualType == pixmapAtom && + actualFormat == 32 && + nItems == 1) + { + Pixmap p; + + memcpy (&p, prop, 4); + + if (p) + { + unsigned int ui; + int i; + Window w; + + if (XGetGeometry (dpy, p, &w, &i, &i, + &width, &height, &ui, &depth)) + { + if (depth == screen->attrib.depth) + pixmap = p; + } + } + } + + XFree (prop); + } + } + + if (!testMode && pixmap) + { + if (pixmap == texture->pixmap) + return; + + finiTexture (screen, texture); + initTexture (screen, texture); + + if (!bindPixmapToTexture (screen, texture, pixmap, + width, height, depth)) + { + fprintf (stderr, "%s: Couldn't bind background pixmap 0x%x to " + "texture\n", programName, (int) pixmap); + } + } + else + { + finiTexture (screen, texture); + initTexture (screen, texture); + } + + if (!texture->name) + readImageToTexture (screen, texture, backgroundImage, &width, &height); + + if (texture->target == GL_TEXTURE_2D) + { + glBindTexture (texture->target, texture->name); + glTexParameteri (texture->target, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri (texture->target, GL_TEXTURE_WRAP_T, GL_REPEAT); + glBindTexture (texture->target, 0); + } +} + +void +detectRefreshRateOfScreen (CompScreen *s) +{ + if (s->opt[COMP_SCREEN_OPTION_DETECT_REFRESH_RATE].value.b) + { + XRRScreenConfiguration *config; + char *name; + CompOptionValue value; + + config = XRRGetScreenInfo (s->display->display, s->root); + value.i = (int) XRRConfigCurrentRate (config); + + XRRFreeScreenConfigInfo (config); + + name = s->opt[COMP_SCREEN_OPTION_REFRESH_RATE].name; + + s->opt[COMP_SCREEN_OPTION_DETECT_REFRESH_RATE].value.b = FALSE; + (*s->setScreenOption) (s, name, &value); + s->opt[COMP_SCREEN_OPTION_DETECT_REFRESH_RATE].value.b = TRUE; + } +} + +static void +setSupportingWmCheck (CompScreen *s) +{ + CompDisplay *d = s->display; + + XChangeProperty (d->display, s->grabWindow, d->supportingWmCheckAtom, + XA_WINDOW, 32, PropModeReplace, + (unsigned char *) &s->grabWindow, 1); + + XChangeProperty (d->display, s->grabWindow, d->wmNameAtom, + d->utf8StringAtom, 8, PropModeReplace, + (unsigned char *) PACKAGE, strlen (PACKAGE)); + XChangeProperty (d->display, s->grabWindow, d->winStateAtom, + XA_ATOM, 32, PropModeReplace, + (unsigned char *) &d->winStateSkipTaskbarAtom, 1); + XChangeProperty (d->display, s->grabWindow, d->winStateAtom, + XA_ATOM, 32, PropModeAppend, + (unsigned char *) &d->winStateSkipPagerAtom, 1); + XChangeProperty (d->display, s->grabWindow, d->winStateAtom, + XA_ATOM, 32, PropModeAppend, + (unsigned char *) &d->winStateHiddenAtom, 1); + + XChangeProperty (d->display, s->root, d->supportingWmCheckAtom, + XA_WINDOW, 32, PropModeReplace, + (unsigned char *) &s->grabWindow, 1); +} + +static void +setSupported (CompScreen *s) +{ + CompDisplay *d = s->display; + Atom data[256]; + int i = 0; + + data[i++] = d->supportedAtom; + data[i++] = d->supportingWmCheckAtom; + + data[i++] = d->utf8StringAtom; + + data[i++] = d->clientListAtom; + data[i++] = d->clientListStackingAtom; + + data[i++] = d->winActiveAtom; + + data[i++] = d->desktopViewportAtom; + data[i++] = d->desktopGeometryAtom; + data[i++] = d->currentDesktopAtom; + data[i++] = d->numberOfDesktopsAtom; + data[i++] = d->showingDesktopAtom; + + data[i++] = d->workareaAtom; + + data[i++] = d->wmNameAtom; +/* + data[i++] = d->wmVisibleNameAtom; +*/ + + data[i++] = d->wmStrutAtom; + data[i++] = d->wmStrutPartialAtom; + +/* + data[i++] = d->wmPidAtom; + data[i++] = d->wmUserTimeAtom; +*/ + + data[i++] = d->frameExtentsAtom; + data[i++] = d->frameWindowAtom; + + data[i++] = d->winStateModalAtom; + data[i++] = d->winStateStickyAtom; + data[i++] = d->winStateMaximizedVertAtom; + data[i++] = d->winStateMaximizedHorzAtom; + data[i++] = d->winStateShadedAtom; + data[i++] = d->winStateSkipTaskbarAtom; + data[i++] = d->winStateSkipPagerAtom; + data[i++] = d->winStateHiddenAtom; + data[i++] = d->winStateFullscreenAtom; + data[i++] = d->winStateAboveAtom; + data[i++] = d->winStateBelowAtom; + data[i++] = d->winStateDemandsAttentionAtom; + + data[i++] = d->winOpacityAtom; + data[i++] = d->winBrightnessAtom; + + if (s->canDoSaturated) + { + data[i++] = d->winSaturationAtom; + data[i++] = d->winStateDisplayModalAtom; + } + + data[i++] = d->wmAllowedActionsAtom; + + data[i++] = d->winActionMoveAtom; + data[i++] = d->winActionResizeAtom; + data[i++] = d->winActionStickAtom; + data[i++] = d->winActionMinimizeAtom; + data[i++] = d->winActionMaximizeHorzAtom; + data[i++] = d->winActionMaximizeVertAtom; + data[i++] = d->winActionFullscreenAtom; + data[i++] = d->winActionCloseAtom; + + data[i++] = d->winTypeAtom; + data[i++] = d->winTypeDesktopAtom; + data[i++] = d->winTypeDockAtom; + data[i++] = d->winTypeToolbarAtom; + data[i++] = d->winTypeMenuAtom; + data[i++] = d->winTypeSplashAtom; + data[i++] = d->winTypeDialogAtom; + data[i++] = d->winTypeUtilAtom; + data[i++] = d->winTypeNormalAtom; + + data[i++] = d->wmDeleteWindowAtom; + data[i++] = d->wmPingAtom; + + data[i++] = d->wmMoveResizeAtom; + data[i++] = d->moveResizeWindowAtom; + + XChangeProperty (d->display, s->root, d->supportedAtom, XA_ATOM, 32, + PropModeReplace, (unsigned char *) data, i); +} + +static void +setDesktopHints (CompScreen *s) +{ + CompDisplay *d = s->display; + unsigned long data[2]; + Atom actual; + int result, format; + unsigned long n, left; + unsigned char *propData; + + result = XGetWindowProperty (s->display->display, s->root, + d->desktopViewportAtom, 0L, 2L, FALSE, + XA_CARDINAL, &actual, &format, + &n, &left, &propData); + + if (result == Success && n && propData) + { + if (n == 2) + { + memcpy (data, propData, sizeof (unsigned long)); + + if (data[0] / s->width < s->size - 1) + s->x = data[0] / s->width; + } + + XFree (propData); + } + + data[0] = s->x * s->width; + data[1] = 0; + + XChangeProperty (d->display, s->root, d->desktopViewportAtom, + XA_CARDINAL, 32, PropModeReplace, + (unsigned char *) data, 2); + + data[0] = s->width * s->size; + data[1] = s->height; + + XChangeProperty (d->display, s->root, d->desktopGeometryAtom, + XA_CARDINAL, 32, PropModeReplace, + (unsigned char *) data, 2); + + data[0] = 1; + + XChangeProperty (d->display, s->root, d->numberOfDesktopsAtom, + XA_CARDINAL, 32, PropModeReplace, + (unsigned char *) data, 1); + + data[0] = 0; + + XChangeProperty (d->display, s->root, d->currentDesktopAtom, + XA_CARDINAL, 32, PropModeReplace, + (unsigned char *) data, 1); + + data[0] = 0; + + result = XGetWindowProperty (s->display->display, s->root, + d->showingDesktopAtom, 0L, 1L, FALSE, + XA_CARDINAL, &actual, &format, + &n, &left, &propData); + + if (result == Success && n && propData) + { + memcpy (data, propData, sizeof (unsigned long)); + XFree (propData); + + if (data[0]) + enterShowDesktopMode (s); + } + + XChangeProperty (d->display, s->root, d->showingDesktopAtom, + XA_CARDINAL, 32, PropModeReplace, + (unsigned char *) data, 1); +} + +Bool +addScreen (CompDisplay *display, + int screenNum, + Window wmSnSelectionWindow, + Atom wmSnAtom, + Time wmSnTimestamp) +{ + CompScreen *s; + Display *dpy = display->display; + static char data = 0; + XColor black; + Pixmap bitmap; + XVisualInfo templ; + XVisualInfo *visinfo; + VisualID visualIDs[MAX_DEPTH + 1]; + Window rootReturn, parentReturn; + Window *children; + unsigned int nchildren; + int defaultDepth, nvisinfo, value, i; + const char *glxExtensions, *glExtensions; + GLint stencilBits; + XSetWindowAttributes attrib; + CompWindow *w; + + s = malloc (sizeof (CompScreen)); + if (!s) + return FALSE; + + s->windowPrivateIndices = 0; + s->windowPrivateLen = 0; + + if (display->screenPrivateLen) + { + s->privates = malloc (display->screenPrivateLen * + sizeof (CompPrivate)); + if (!s->privates) + { + free (s); + return FALSE; + } + } + else + s->privates = 0; + + s->display = display; + + compScreenInitOptions (s); + + s->damage = XCreateRegion (); + if (!s->damage) + return FALSE; + + s->x = 0; + s->size = 4; + + s->buttonGrab = 0; + s->nButtonGrab = 0; + s->keyGrab = 0; + s->nKeyGrab = 0; + + s->grabs = 0; + s->grabSize = 0; + s->maxGrab = 0; + + s->pendingDestroys = 0; + + s->clientList = 0; + s->nClientList = 0; + + s->screenNum = screenNum; + s->colormap = DefaultColormap (dpy, screenNum); + s->root = XRootWindow (dpy, screenNum); + + s->mapNum = 1; + s->activeNum = 1; + + s->groups = NULL; + + s->snContext = sn_monitor_context_new (display->snDisplay, + screenNum, + compScreenSnEvent, s, + NULL); + + s->startupSequences = NULL; + s->startupSequenceTimeoutHandle = 0; + + s->wmSnSelectionWindow = wmSnSelectionWindow; + s->wmSnAtom = wmSnAtom; + s->wmSnTimestamp = wmSnTimestamp; + + if (testMode) + { + XWMHints *wmHints; + XSizeHints *normalHints; + XClassHint *classHint; + int glx_attrib[] = { + GLX_RGBA, + GLX_RED_SIZE, 1, + GLX_STENCIL_SIZE, 2, + GLX_DOUBLEBUFFER, + None + }; + + visinfo = glXChooseVisual (dpy, screenNum, glx_attrib); + if (!visinfo) + { + int glx_attrib2[] = { + GLX_RGBA, + GLX_RED_SIZE, 1, + GLX_DOUBLEBUFFER, + None + }; + + visinfo = glXChooseVisual (dpy, screenNum, glx_attrib2); + if (!visinfo) + { + fprintf (stderr, "%s: Couldn't find a double buffered " + "RGB visual.\n", programName); + return FALSE; + } + } + + attrib.colormap = XCreateColormap (dpy, s->root, visinfo->visual, + AllocNone); + + normalHints = XAllocSizeHints (); + normalHints->flags = 0; + normalHints->x = 0; + normalHints->y = 0; + normalHints->width = 800; + normalHints->height = 600; + + classHint = XAllocClassHint (); + classHint->res_name = "compiz"; + classHint->res_class = "Compiz"; + + wmHints = XAllocWMHints (); + wmHints->flags = InputHint; + wmHints->input = TRUE; + + s->root = XCreateWindow (dpy, s->root, 0, 0, + normalHints->width, normalHints->height, 0, + visinfo->depth, InputOutput, visinfo->visual, + CWColormap, &attrib); + + XSelectInput (dpy, s->root, + SubstructureNotifyMask | + ExposureMask | + ButtonPressMask | + ButtonReleaseMask | + ButtonMotionMask); + + XRRSelectInput (dpy, s->root, RRScreenChangeNotifyMask); + + XSetWMProtocols (dpy, s->root, &display->wmDeleteWindowAtom, 1); + + XmbSetWMProperties (dpy, s->root, + "glxcompmgr - Test mode", "glxcompmgr", + programArgv, programArgc, + normalHints, wmHints, classHint); + + XMapWindow (dpy, s->root); + + XFree (wmHints); + XFree (classHint); + XFree (normalHints); + } + + s->fake[0] = s->fake[1] = 0; + + s->escapeKeyCode = XKeysymToKeycode (display->display, + XStringToKeysym ("Escape")); + + s->damageMask = COMP_SCREEN_DAMAGE_ALL_MASK; + s->next = 0; + s->exposeRects = 0; + s->sizeExpose = 0; + s->nExpose = 0; + + s->rasterX = 0; + s->rasterY = 0; + + s->windows = 0; + s->reverseWindows = 0; + + s->stencilRef = 0x1; + + s->nextRedraw = 0; + s->frameStatus = 0; + + s->showingDesktopMask = 0; + + gettimeofday (&s->lastRedraw, 0); + + s->setScreenOption = setScreenOption; + s->setScreenOptionForPlugin = setScreenOptionForPlugin; + + s->initPluginForScreen = initPluginForScreen; + s->finiPluginForScreen = finiPluginForScreen; + + s->preparePaintScreen = preparePaintScreen; + s->donePaintScreen = donePaintScreen; + s->paintScreen = paintScreen; + s->paintTransformedScreen = paintTransformedScreen; + s->paintBackground = paintBackground; + s->paintWindow = paintWindow; + s->addWindowGeometry = addWindowGeometry; + s->drawWindowGeometry = drawWindowGeometry; + s->damageWindowRect = damageWindowRect; + s->focusWindow = focusWindow; + s->setWindowScale = setWindowScale; + + s->windowResizeNotify = windowResizeNotify; + s->windowMoveNotify = windowMoveNotify; + s->windowGrabNotify = windowGrabNotify; + s->windowUngrabNotify = windowUngrabNotify; + + s->getProcAddress = 0; + + if (!XGetWindowAttributes (dpy, s->root, &s->attrib)) + return FALSE; + + s->workArea.x = 0; + s->workArea.y = 0; + s->workArea.width = s->attrib.width; + s->workArea.height = s->attrib.height; + + s->grabWindow = None; + + templ.visualid = XVisualIDFromVisual (s->attrib.visual); + + visinfo = XGetVisualInfo (dpy, VisualIDMask, &templ, &nvisinfo); + if (!nvisinfo) + { + fprintf (stderr, "%s: Couldn't get visual info for default visual\n", + programName); + return FALSE; + } + + defaultDepth = visinfo->depth; + + black.red = black.green = black.blue = 0; + + if (!XAllocColor (dpy, s->colormap, &black)) + { + fprintf (stderr, "%s: Couldn't allocate color\n", programName); + return FALSE; + } + + bitmap = XCreateBitmapFromData (dpy, s->root, &data, 1, 1); + if (!bitmap) + { + fprintf (stderr, "%s: Couldn't create bitmap\n", programName); + return FALSE; + } + + s->invisibleCursor = XCreatePixmapCursor (dpy, bitmap, bitmap, + &black, &black, 0, 0); + if (!s->invisibleCursor) + { + fprintf (stderr, "%s: Couldn't create invisible cursor\n", + programName); + return FALSE; + } + + XFreePixmap (dpy, bitmap); + XFreeColors (dpy, s->colormap, &black.pixel, 1, 0); + + glXGetConfig (dpy, visinfo, GLX_USE_GL, &value); + if (!value) + { + fprintf (stderr, "%s: Root visual is not a GL visual\n", + programName); + return FALSE; + } + + addScreenBinding (s, &s->opt[COMP_SCREEN_OPTION_CLOSE_WINDOW].value.bind); + addScreenBinding (s, &s->opt[COMP_SCREEN_OPTION_MAIN_MENU].value.bind); + addScreenBinding (s, &s->opt[COMP_SCREEN_OPTION_RUN_DIALOG].value.bind); + + glXGetConfig (dpy, visinfo, GLX_DOUBLEBUFFER, &value); + if (!value) + { + fprintf (stderr, + "%s: Root visual is not a double buffered GL visual\n", + programName); + return FALSE; + } + + s->ctx = glXCreateContext (dpy, visinfo, NULL, TRUE); + if (!s->ctx) + { + fprintf (stderr, "%s: glXCreateContext failed\n", programName); + return FALSE; + } + + XFree (visinfo); + + /* we don't want to allocate back, stencil or depth buffers for pixmaps + so lets see if we can find an approriate visual without these buffers */ + for (i = 0; i <= MAX_DEPTH; i++) + { + int j, db, stencil, depth; + + visualIDs[i] = 0; + + db = MAXSHORT; + stencil = MAXSHORT; + depth = MAXSHORT; + + templ.depth = i; + + visinfo = XGetVisualInfo (dpy, VisualDepthMask, &templ, &nvisinfo); + for (j = 0; j < nvisinfo; j++) + { + glXGetConfig (dpy, &visinfo[j], GLX_USE_GL, &value); + if (!value) + continue; + + glXGetConfig (dpy, &visinfo[j], GLX_DOUBLEBUFFER, &value); + if (value > db) + continue; + + db = value; + glXGetConfig (dpy, &visinfo[j], GLX_STENCIL_SIZE, &value); + if (value > stencil) + continue; + + stencil = value; + glXGetConfig (dpy, &visinfo[j], GLX_DEPTH_SIZE, &value); + if (value > depth) + continue; + + depth = value; + visualIDs[i] = visinfo[j].visualid; + } + + if (nvisinfo) + XFree (visinfo); + } + + /* create contexts for supported depths */ + for (i = 0; i <= MAX_DEPTH; i++) + { + templ.visualid = visualIDs[i]; + s->glxPixmapVisuals[i] = XGetVisualInfo (dpy, + VisualIDMask, + &templ, + &nvisinfo); + } + + if (!s->glxPixmapVisuals[defaultDepth]) + { + fprintf (stderr, "%s: No GL visual for default depth, " + "this isn't going to work.\n", programName); + return FALSE; + } + + glXMakeCurrent (dpy, s->root, s->ctx); + currentRoot = s->root; + + glxExtensions = glXQueryExtensionsString (s->display->display, screenNum); + if (!testMode && !strstr (glxExtensions, "GLX_EXT_texture_from_pixmap")) + { + fprintf (stderr, "%s: GLX_EXT_texture_from_pixmap is missing\n", + programName); + return FALSE; + } + + s->getProcAddress = (GLXGetProcAddressProc) + getProcAddress (s, "glXGetProcAddressARB"); + s->bindTexImage = (GLXBindTexImageProc) + getProcAddress (s, "glXBindTexImageEXT"); + s->releaseTexImage = (GLXReleaseTexImageProc) + getProcAddress (s, "glXReleaseTexImageEXT"); + s->queryDrawable = (GLXQueryDrawableProc) + getProcAddress (s, "glXQueryDrawable"); + + if (!testMode && !s->bindTexImage) + { + fprintf (stderr, "%s: glXBindTexImageEXT is missing\n", programName); + return FALSE; + } + + if (!testMode && !s->releaseTexImage) + { + fprintf (stderr, "%s: glXReleaseTexImageEXT is missing\n", + programName); + return FALSE; + } + + if (!testMode && !s->queryDrawable) + { + fprintf (stderr, "%s: glXQueryDrawable is missing\n", programName); + return FALSE; + } + + s->textureRectangle = 0; + glExtensions = (const char *) glGetString (GL_EXTENSIONS); + if (strstr (glExtensions, "GL_NV_texture_rectangle") || + strstr (glExtensions, "GL_EXT_texture_rectangle") || + strstr (glExtensions, "GL_ARB_texture_rectangle")) + s->textureRectangle = 1; + + s->textureNonPowerOfTwo = 0; + if (strstr (glExtensions, "GL_ARB_texture_non_power_of_two")) + s->textureNonPowerOfTwo = 1; + + if (!(s->textureRectangle || s->textureNonPowerOfTwo)) + { + fprintf (stderr, "%s: Support for non power of two textures missing\n", + programName); + return FALSE; + } + + s->textureEnvCombine = s->textureEnvCrossbar = 0; + if (strstr (glExtensions, "GL_ARB_texture_env_combine")) + { + s->textureEnvCombine = 1; + + /* XXX: GL_NV_texture_env_combine4 need special code but it seams to + be working anyway for now... */ + if (strstr (glExtensions, "GL_ARB_texture_env_crossbar") || + strstr (glExtensions, "GL_NV_texture_env_combine4")) + s->textureEnvCrossbar = 1; + } + + s->textureBorderClamp = 0; + if (strstr (glExtensions, "GL_ARB_texture_border_clamp") || + strstr (glExtensions, "GL_SGIS_texture_border_clamp")) + s->textureBorderClamp = 1; + + s->maxTextureUnits = 1; + if (strstr (glExtensions, "GL_ARB_multitexture")) + { + s->activeTexture = (GLActiveTextureProc) + getProcAddress (s, "glActiveTexture"); + s->clientActiveTexture = (GLClientActiveTextureProc) + getProcAddress (s, "glClientActiveTexture"); + + if (s->activeTexture && s->clientActiveTexture) + glGetIntegerv (GL_MAX_TEXTURE_UNITS_ARB, &s->maxTextureUnits); + } + + initTexture (s, &s->backgroundTexture); + + s->desktopWindowCount = 0; + + glGetIntegerv (GL_STENCIL_BITS, &stencilBits); + if (!stencilBits) + { + fprintf (stderr, "%s: No stencil buffer. Clipping of transformed " + "windows is not going to be correct when screen is " + "transformed.\n", programName); + } + + glClearColor (0.0, 0.0, 0.0, 1.0); + glBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + glEnable (GL_CULL_FACE); + glDisable (GL_BLEND); + glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glColor4usv (defaultColor); + glEnableClientState (GL_VERTEX_ARRAY); + glEnableClientState (GL_TEXTURE_COORD_ARRAY); + + s->canDoSaturated = s->canDoSlightlySaturated = FALSE; + if (s->textureEnvCombine && s->maxTextureUnits >= 2) + { + s->canDoSaturated = TRUE; + if (s->textureEnvCrossbar && s->maxTextureUnits >= 4) + s->canDoSlightlySaturated = TRUE; + } + + s->redrawTime = 1000 / defaultRefreshRate; + s->optimalRedrawTime = s->redrawTime; + + reshape (s, s->attrib.width, s->attrib.height); + + s->next = display->screens; + display->screens = s; + + screenInitPlugins (s); + + detectRefreshRateOfScreen (s); + + XGrabServer (dpy); + + XQueryTree (dpy, s->root, + &rootReturn, &parentReturn, + &children, &nchildren); + + for (i = 0; i < nchildren; i++) + addWindow (s, children[i], i ? children[i - 1] : 0); + + for (w = s->windows; w; w = w->next) + { + if (w->attrib.map_state == IsViewable) + { + w->activeNum = s->activeNum++; + w->damaged = TRUE; + w->placed = TRUE; + w->invisible = WINDOW_INVISIBLE (w); + } + else if (w->state & CompWindowStateHiddenMask) + { + w->placed = TRUE; + } + } + + XUngrabServer (dpy); + + XFree (children); + + attrib.override_redirect = 1; + s->grabWindow = XCreateWindow (dpy, s->root, -100, -100, 1, 1, 0, + CopyFromParent, InputOnly, + CopyFromParent, CWOverrideRedirect, + &attrib); + + XMapWindow (dpy, s->grabWindow); + + if (testMode) + { + s->fake[0] = XCreateWindow (dpy, s->root, 64, 32, 1, 1, 0, + CopyFromParent, InputOutput, + CopyFromParent, 0, NULL); + + s->fake[1] = XCreateWindow (dpy, s->root, 256, 256, 1, 1, 0, + CopyFromParent, InputOutput, + CopyFromParent, 0, NULL); + } + + setDesktopHints (s); + setSupportingWmCheck (s); + setSupported (s); + + s->normalCursor = XCreateFontCursor (dpy, XC_left_ptr); + s->busyCursor = XCreateFontCursor (dpy, XC_watch); + + XDefineCursor (dpy, s->root, s->normalCursor); + + return TRUE; +} + +void +damageScreenRegion (CompScreen *screen, + Region region) +{ + if (screen->damageMask & COMP_SCREEN_DAMAGE_ALL_MASK) + return; + + XUnionRegion (screen->damage, region, screen->damage); + + screen->damageMask |= COMP_SCREEN_DAMAGE_REGION_MASK; +} + +void +damageScreen (CompScreen *s) +{ + s->damageMask |= COMP_SCREEN_DAMAGE_ALL_MASK; + s->damageMask &= ~COMP_SCREEN_DAMAGE_REGION_MASK; +} + +void +damagePendingOnScreen (CompScreen *s) +{ + s->damageMask |= COMP_SCREEN_DAMAGE_PENDING_MASK; +} + +void +forEachWindowOnScreen (CompScreen *screen, + ForEachWindowProc proc, + void *closure) +{ + CompWindow *w; + + for (w = screen->windows; w; w = w->next) + (*proc) (w, closure); +} + +CompWindow * +findWindowAtScreen (CompScreen *s, + Window id) +{ + if (lastFoundWindow && lastFoundWindow->id == id) + { + return lastFoundWindow; + } + else + { + CompWindow *w; + + for (w = s->windows; w; w = w->next) + if (w->id == id) + return (lastFoundWindow = w); + } + + return 0; +} + +CompWindow * +findTopLevelWindowAtScreen (CompScreen *s, + Window id) +{ + CompWindow *found = NULL; + + if (lastFoundWindow && lastFoundWindow->id == id) + { + found = lastFoundWindow; + } + else + { + CompWindow *w; + + for (w = s->windows; w; w = w->next) + { + if (w->id == id) + { + found = (lastFoundWindow = w); + break; + } + } + } + + if (!found) + return NULL; + + if (found->attrib.override_redirect) + { + /* likely a frame window */ + if (found->attrib.class == InputOnly) + { + CompWindow *w; + + for (w = s->windows; w; w = w->next) + if (w->frame == id) + return w; + } + + return NULL; + } + + return found; +} + +void +insertWindowIntoScreen (CompScreen *s, + CompWindow *w, + Window aboveId) +{ + CompWindow *p; + + if (s->windows) + { + if (!aboveId) + { + w->next = s->windows; + w->prev = NULL; + s->windows->prev = w; + s->windows = w; + } + else + { + for (p = s->windows; p; p = p->next) + { + if (p->next) + { + if (p->id == aboveId && p->mapNum) + { + w->next = p->next; + w->prev = p; + p->next->prev = w; + p->next = w; + + break; + } + } + else + { + p->next = w; + w->next = NULL; + w->prev = p; + s->reverseWindows = w; + + break; + } + } + } + } + else + { + s->reverseWindows = s->windows = w; + w->prev = w->next = NULL; + } +} + +void +unhookWindowFromScreen (CompScreen *s, + CompWindow *w) +{ + CompWindow *next, *prev; + + next = w->next; + prev = w->prev; + + if (next || prev) + { + if (next) + { + if (prev) + { + next->prev = prev; + } + else + { + s->windows = next; + next->prev = NULL; + } + } + + if (prev) + { + if (next) + { + prev->next = next; + } + else + { + s->reverseWindows = prev; + prev->next = NULL; + } + } + } + else + { + s->windows = s->reverseWindows = NULL; + } + + if (w == lastFoundWindow) + lastFoundWindow = NULL; + if (w == lastDamagedWindow) + lastDamagedWindow = NULL; +} + +#define POINTER_GRAB_MASK (ButtonReleaseMask | \ + ButtonPressMask | \ + PointerMotionMask) +int +pushScreenGrab (CompScreen *s, + Cursor cursor) +{ + if (s->maxGrab == 0) + { + int status; + + status = XGrabPointer (s->display->display, s->grabWindow, TRUE, + POINTER_GRAB_MASK, + GrabModeAsync, GrabModeAsync, + s->root, cursor, + CurrentTime); + + if (status == GrabSuccess) + { + status = XGrabKeyboard (s->display->display, + s->grabWindow, TRUE, + GrabModeAsync, GrabModeAsync, + CurrentTime); + if (status != GrabSuccess) + { + XUngrabPointer (s->display->display, CurrentTime); + return 0; + } + } + else + return 0; + } + else + { + XChangeActivePointerGrab (s->display->display, POINTER_GRAB_MASK, + cursor, CurrentTime); + } + + if (s->grabSize <= s->maxGrab) + { + s->grabs = realloc (s->grabs, sizeof (CompGrab) * (s->maxGrab + 1)); + if (!s->grabs) + return 0; + + s->grabSize = s->maxGrab + 1; + } + + s->grabs[s->maxGrab].cursor = cursor; + s->grabs[s->maxGrab].active = TRUE; + + s->maxGrab++; + + return s->maxGrab; +} + +void +removeScreenGrab (CompScreen *s, + int index, + XPoint *restorePointer) +{ + int maxGrab; + + index--; + if (index < 0 || index >= s->maxGrab) + abort (); + + s->grabs[index].cursor = None; + s->grabs[index].active = FALSE; + + for (maxGrab = s->maxGrab; maxGrab; maxGrab--) + if (s->grabs[maxGrab - 1].active) + break; + + if (maxGrab != s->maxGrab) + { + if (maxGrab) + { + XChangeActivePointerGrab (s->display->display, + POINTER_GRAB_MASK, + s->grabs[s->maxGrab - 1].cursor, + CurrentTime); + } + else + { + if (restorePointer) + XWarpPointer (s->display->display, None, s->root, 0, 0, 0, 0, + restorePointer->x, restorePointer->y); + + XUngrabPointer (s->display->display, CurrentTime); + XUngrabKeyboard (s->display->display, CurrentTime); + } + s->maxGrab = maxGrab; + } +} + +static Bool +addPassiveKeyGrab (CompScreen *s, + CompKeyBinding *key) +{ + CompKeyGrab *keyGrab; + unsigned int modifiers, mask, ignore; + int i; + + modifiers = key->modifiers & ~(CompPressMask | CompReleaseMask); + if (modifiers == key->modifiers) + return TRUE; + + for (i = 0; i < s->nKeyGrab; i++) + { + if (key->keycode == s->keyGrab[i].keycode && + modifiers == s->keyGrab[i].modifiers) + { + s->keyGrab[i].count++; + return TRUE; + } + } + + keyGrab = realloc (s->keyGrab, sizeof (CompKeyGrab) * (s->nKeyGrab + 1)); + if (!keyGrab) + return FALSE; + + s->keyGrab = keyGrab; + + mask = virtualToRealModMask (s->display, modifiers); + if (!(mask & CompNoMask)) + { + compCheckForError (s->display->display); + + for (ignore = 0; ignore <= s->display->ignoredModMask; ignore++) + { + if (ignore & ~s->display->ignoredModMask) + continue; + + XGrabKey (s->display->display, + key->keycode, + mask | ignore, + s->root, + TRUE, + GrabModeAsync, + GrabModeAsync); + } + + if (compCheckForError (s->display->display)) + { + +#ifdef DEBUG + KeySym keysym; + char *keyname; + + keysym = XKeycodeToKeysym (s->display->display, + key->keycode, + 0); + keyname = XKeysymToString (keysym); + + fprintf (stderr, "XGrabKey failed: %s 0x%x\n", + keyname, modifiers); +#endif + + return FALSE; + } + } + + s->keyGrab[s->nKeyGrab].keycode = key->keycode; + s->keyGrab[s->nKeyGrab].modifiers = modifiers; + s->keyGrab[s->nKeyGrab].count = 1; + + s->nKeyGrab++; + + return TRUE; +} + +static void +removePassiveKeyGrab (CompScreen *s, + CompKeyBinding *key) +{ + unsigned int modifiers, mask, ignore; + int i; + + modifiers = key->modifiers & ~(CompPressMask | CompReleaseMask); + if (modifiers == key->modifiers) + return; + + for (i = 0; i < s->nKeyGrab; i++) + { + if (key->keycode == s->keyGrab[i].keycode && + modifiers == s->keyGrab[i].modifiers) + { + s->keyGrab[i].count--; + if (s->keyGrab[i].count) + return; + + s->nKeyGrab--; + s->keyGrab = realloc (s->keyGrab, + sizeof (CompKeyGrab) * s->nKeyGrab); + + mask = virtualToRealModMask (s->display, modifiers); + if (!(mask & CompNoMask)) + { + for (ignore = 0; ignore <= s->display->ignoredModMask; ignore++) + { + if (ignore & ~s->display->ignoredModMask) + continue; + + XUngrabKey (s->display->display, + key->keycode, + mask | ignore, + s->root); + } + } + } + } +} + +static void +updatePassiveKeyGrabs (CompScreen *s) +{ + unsigned int mask, ignore; + int i; + + XUngrabKey (s->display->display, AnyKey, AnyModifier, s->root); + + for (i = 0; i < s->nKeyGrab; i++) + { + mask = virtualToRealModMask (s->display, s->keyGrab[i].modifiers); + if (!(mask & CompNoMask)) + { + for (ignore = 0; ignore <= s->display->ignoredModMask; ignore++) + { + if (ignore & ~s->display->ignoredModMask) + continue; + + XGrabKey (s->display->display, + s->keyGrab[i].keycode, + mask | ignore, + s->root, + TRUE, + GrabModeAsync, + GrabModeAsync); + } + } + } +} + +static Bool +addPassiveButtonGrab (CompScreen *s, + CompButtonBinding *button) +{ + CompButtonGrab *buttonGrab; + unsigned int modifiers; + int i; + + modifiers = button->modifiers & ~(CompPressMask | CompReleaseMask); + if (modifiers == button->modifiers) + return TRUE; + + for (i = 0; i < s->nButtonGrab; i++) + { + if (button->button == s->buttonGrab[i].button && + modifiers == s->buttonGrab[i].modifiers) + { + s->buttonGrab[i].count++; + return TRUE; + } + } + + buttonGrab = realloc (s->buttonGrab, + sizeof (CompButtonGrab) * (s->nButtonGrab + 1)); + if (!buttonGrab) + return FALSE; + + s->buttonGrab = buttonGrab; + + s->buttonGrab[s->nButtonGrab].button = button->button; + s->buttonGrab[s->nButtonGrab].modifiers = modifiers; + s->buttonGrab[s->nButtonGrab].count = 1; + + s->nButtonGrab++; + + return TRUE; +} + +static void +removePassiveButtonGrab (CompScreen *s, + CompButtonBinding *button) +{ + unsigned int modifiers; + int i; + + modifiers = button->modifiers & ~(CompPressMask | CompReleaseMask); + if (modifiers == button->modifiers) + return; + + for (i = 0; i < s->nButtonGrab; i++) + { + if (button->button == s->buttonGrab[i].button && + modifiers == s->buttonGrab[i].modifiers) + { + s->buttonGrab[i].count--; + if (s->buttonGrab[i].count) + return; + + s->nButtonGrab--; + s->buttonGrab = realloc (s->buttonGrab, + sizeof (CompButtonGrab) * s->nButtonGrab); + } + } +} + +Bool +addScreenBinding (CompScreen *s, + CompBinding *binding) +{ + if (binding->type == CompBindingTypeKey) + return addPassiveKeyGrab (s, &binding->u.key); + else if (binding->type == CompBindingTypeButton) + return addPassiveButtonGrab (s, &binding->u.button); + + return FALSE; +} + +void +removeScreenBinding (CompScreen *s, + CompBinding *binding) +{ + if (binding->type == CompBindingTypeKey) + removePassiveKeyGrab (s, &binding->u.key); + else if (binding->type == CompBindingTypeButton) + removePassiveButtonGrab (s, &binding->u.button); +} + +void +updatePassiveGrabs (CompScreen *s) +{ + updatePassiveKeyGrabs (s); +} + +void +updateWorkareaForScreen (CompScreen *s) +{ + CompWindow *w; + int leftStrut, rightStrut, topStrut, bottomStrut; + XRectangle workArea; + + leftStrut = 0; + rightStrut = 0; + topStrut = 0; + bottomStrut = 0; + + for (w = s->windows; w; w = w->next) + { + if (w->attrib.map_state == IsUnmapped) + continue; + + if (w->struts) + { + if (w->struts->left.width > leftStrut) + leftStrut = w->struts->left.width; + + if (w->struts->right.width > rightStrut) + rightStrut = w->struts->right.width; + + if (w->struts->top.height > topStrut) + topStrut = w->struts->top.height; + + if (w->struts->bottom.height > bottomStrut) + bottomStrut = w->struts->bottom.height; + } + } + +#define MIN_SANE_AREA 100 + + if ((leftStrut + rightStrut) > (s->width - MIN_SANE_AREA)) + { + leftStrut = (s->width - MIN_SANE_AREA) / 2; + rightStrut = leftStrut; + } + + if ((topStrut + bottomStrut) > (s->height - MIN_SANE_AREA)) + { + topStrut = (s->height - MIN_SANE_AREA) / 2; + bottomStrut = topStrut; + } + + workArea.x = leftStrut; + workArea.y = topStrut; + workArea.width = s->width - leftStrut - rightStrut; + workArea.height = s->height - topStrut - bottomStrut; + + if (memcmp (&workArea, &s->workArea, sizeof (XRectangle))) + { + unsigned long data[4]; + + data[0] = workArea.x; + data[1] = workArea.y; + data[2] = workArea.width; + data[3] = workArea.height; + + XChangeProperty (s->display->display, s->root, + s->display->workareaAtom, XA_CARDINAL, 32, + PropModeReplace, (unsigned char *) data, 4); + + s->workArea = workArea; + } +} + +static Bool +isClientListWindow (CompWindow *w) +{ + if (w->attrib.override_redirect) + return FALSE; + + if (w->attrib.map_state != IsViewable) + { + if (!(w->state & CompWindowStateHiddenMask)) + return FALSE; + } + + if (w->type & CompWindowTypeNormalMask) + return TRUE; + + return FALSE; +} + +static void +countClientListWindow (CompWindow *w, + void *closure) +{ + if (isClientListWindow (w)) + { + int *num = (int *) closure; + + *num = *num + 1; + } +} + +static void +addClientListWindow (CompWindow *w, + void *closure) +{ + if (isClientListWindow (w)) + { + int *num = (int *) closure; + + w->screen->clientList[*num] = w; + *num = *num + 1; + } +} + +static int +compareMappingOrder (const void *w1, + const void *w2) +{ + return (*((CompWindow **) w1))->mapNum - (*((CompWindow **) w2))->mapNum; +} + +void +updateClientListForScreen (CompScreen *s) +{ + Window *clientList; + Window *clientListStacking; + Bool updateClientList = FALSE; + Bool updateClientListStacking = FALSE; + int i, n = 0; + + forEachWindowOnScreen (s, countClientListWindow, (void *) &n); + + if (n == 0) + { + if (n != s->nClientList) + { + free (s->clientList); + + s->clientList = NULL; + s->nClientList = 0; + + XChangeProperty (s->display->display, s->root, + s->display->clientListAtom, + XA_WINDOW, 32, PropModeReplace, + (unsigned char *) &s->grabWindow, 1); + XChangeProperty (s->display->display, s->root, + s->display->clientListStackingAtom, + XA_WINDOW, 32, PropModeReplace, + (unsigned char *) &s->grabWindow, 1); + } + + return; + } + + if (n != s->nClientList) + { + CompWindow **list; + + list = realloc (s->clientList, + (sizeof (CompWindow *) + sizeof (Window) * 2) * n); + if (!list) + return; + + s->clientList = list; + s->nClientList = n; + + updateClientList = updateClientListStacking = TRUE; + } + + clientList = (Window *) (s->clientList + n); + clientListStacking = clientList + n; + + i = 0; + forEachWindowOnScreen (s, addClientListWindow, (void *) &i); + + for (i = 0; i < n; i++) + { + if (!updateClientListStacking) + { + if (clientListStacking[i] != s->clientList[i]->id) + updateClientListStacking = TRUE; + } + + clientListStacking[i] = s->clientList[i]->id; + } + + /* sort window list in mapping order */ + qsort (s->clientList, n, sizeof (CompWindow *), compareMappingOrder); + + for (i = 0; i < n; i++) + { + if (!updateClientList) + { + if (clientList[i] != s->clientList[i]->id) + updateClientList = TRUE; + } + + clientList[i] = s->clientList[i]->id; + } + + if (updateClientList) + XChangeProperty (s->display->display, s->root, + s->display->clientListAtom, + XA_WINDOW, 32, PropModeReplace, + (unsigned char *) clientList, s->nClientList); + + if (updateClientListStacking) + XChangeProperty (s->display->display, s->root, + s->display->clientListStackingAtom, + XA_WINDOW, 32, PropModeReplace, + (unsigned char *) clientListStacking, s->nClientList); +} + +Window +getActiveWindow (CompDisplay *display, + Window root) +{ + Atom actual; + int result, format; + unsigned long n, left; + unsigned char *data; + Window w = None; + + result = XGetWindowProperty (display->display, root, + display->winActiveAtom, 0L, 1L, FALSE, + XA_WINDOW, &actual, &format, + &n, &left, &data); + + if (result == Success && n && data) + { + memcpy (&w, data, sizeof (Window)); + XFree (data); + } + + return w; +} + +void +closeActiveWindow (CompScreen *s) +{ + CompWindow *w; + + w = findWindowAtDisplay (s->display, s->display->activeWindow); + if (w) + closeWindow (w); +} + +void +panelAction (CompScreen *s, + Atom panelAction) +{ + XEvent ev; + + ev.type = ClientMessage; + ev.xclient.window = s->root; + ev.xclient.message_type = s->display->panelActionAtom; + ev.xclient.format = 32; + ev.xclient.data.l[0] = panelAction; + ev.xclient.data.l[1] = CurrentTime; + ev.xclient.data.l[2] = 0; + ev.xclient.data.l[3] = 0; + ev.xclient.data.l[4] = 0; + + XUngrabPointer (s->display->display, CurrentTime); + XUngrabKeyboard (s->display->display, CurrentTime); + + XSendEvent (s->display->display, s->root, FALSE, StructureNotifyMask, &ev); +} + +void +runCommand (CompScreen *s, + const char *command) +{ + if (*command == '\0') + return; + + if (fork () == 0) + { + setsid (); + putenv (s->display->displayString); + execl ("/bin/sh", "/bin/sh", "-c", command, NULL); + exit (0); + } +} + +void +moveScreenViewport (CompScreen *s, + int tx, + Bool sync) +{ + tx = s->x - tx; + tx = MOD (tx, s->size); + tx -= s->x; + + if (tx) + { + CompWindow *w; + int m, wx, vWidth = s->width * s->size; + + s->x += tx; + + tx *= -s->width; + + for (w = s->windows; w; w = w->next) + { + if (w->attrib.map_state != IsViewable) + continue; + + if (w->attrib.override_redirect) + continue; + + if (w->type & (CompWindowTypeDesktopMask | CompWindowTypeDockMask)) + continue; + + if (w->state & CompWindowStateStickyMask) + continue; + + m = w->attrib.x + tx; + if (m - w->output.left < s->width - vWidth) + wx = tx + vWidth; + else if (m + w->width + w->output.right > vWidth) + wx = tx - vWidth; + else + wx = tx; + + if (w->saveMask & CWX) + w->saveWc.x += wx; + + moveWindow (w, wx, 0, sync); + + if (sync) + syncWindowPosition (w); + } + + if (sync) + { + unsigned long data[2]; + + data[0] = s->x * s->width; + data[1] = 0; + + XChangeProperty (s->display->display, s->root, + s->display->desktopViewportAtom, + XA_CARDINAL, 32, PropModeReplace, + (unsigned char *) data, 2); + } + } +} + +void +moveWindowToViewportPosition (CompWindow *w, + int x, + Bool sync) +{ + int tx, vWidth = w->screen->width * w->screen->size; + + x += w->screen->x * w->screen->width; + x = MOD (x, vWidth); + x -= w->screen->x * w->screen->width; + + tx = x - w->attrib.x; + if (tx) + { + int m, wx; + + if (w->attrib.map_state != IsViewable) + return; + + if (w->attrib.override_redirect) + return; + + if (w->type & (CompWindowTypeDesktopMask | CompWindowTypeDockMask)) + return; + + if (w->state & CompWindowStateStickyMask) + return; + + m = w->attrib.x + tx; + if (m - w->output.left < w->screen->width - vWidth) + wx = tx + vWidth; + else if (m + w->width + w->output.right > vWidth) + wx = tx - vWidth; + else + wx = tx; + + if (w->saveMask & CWX) + w->saveWc.x += wx; + + moveWindow (w, wx, 0, sync); + + if (sync) + syncWindowPosition (w); + } +} + +CompGroup * +addGroupToScreen (CompScreen *s, + Window id) +{ + CompGroup *group; + + group = malloc (sizeof (CompGroup)); + if (!group) + return NULL; + + group->next = s->groups; + group->refCnt = 1; + group->id = id; + + s->groups = group; + + return group; +} + +void +removeGroupFromScreen (CompScreen *s, + CompGroup *group) +{ + group->refCnt--; + if (group->refCnt) + return; + + if (group == s->groups) + { + s->groups = group->next; + } + else + { + CompGroup *g; + + for (g = s->groups; g; g = g->next) + { + if (g->next == group) + { + g->next = group->next; + break; + } + } + } + + free (group); +} + +CompGroup * +findGroupAtScreen (CompScreen *s, + Window id) +{ + CompGroup *g; + + for (g = s->groups; g; g = g->next) + if (g->id == id) + return g; + + return NULL; +} + +void +applyStartupProperties (CompScreen *screen, + CompWindow *window) +{ + CompStartupSequence *s = NULL; + const char *startupId = window->startupId; + + printf ("Applying startup props to %d id \"%s\"\n", + (int) window->id, window->startupId ? window->startupId : "(none)"); + + if (!startupId) + { + const char *wmClass; + + for (s = screen->startupSequences; s; s = s->next) + { + wmClass = sn_startup_sequence_get_wmclass (s->sequence); + if (!wmClass) + continue; + + if ((window->resClass && strcmp (wmClass, window->resClass) == 0) || + (window->resName && strcmp (wmClass, window->resName) == 0)) + { + startupId = sn_startup_sequence_get_id (s->sequence); + + window->startupId = strdup (startupId); + + printf ("Ending legacy sequence %s due to window %d\n", + startupId, (int) window->id); + + sn_startup_sequence_complete (s->sequence); + break; + } + } + + if (!startupId) + return; + } + + if (!s) + { + const char *id; + + for (s = screen->startupSequences; s; s = s->next) + { + id = sn_startup_sequence_get_id (s->sequence); + + if (strcmp (id, startupId) == 0) + break; + } + } + + if (s) + { + printf ("Found startup sequence for window %d ID \"%s\"\n", + (int) window->id, startupId); + } + else + { + printf ("Did not find startup sequence for window %d ID \"%s\"\n", + (int) window->id, startupId); + } +} + +void +enterShowDesktopMode (CompScreen *s) +{ + CompWindow *w; + unsigned long data = 1; + + s->showingDesktopMask = ~(CompWindowTypeDesktopMask | + CompWindowTypeDockMask); + + for (w = s->windows; w; w = w->next) + hideWindow (w); + + XChangeProperty (s->display->display, s->root, + s->display->showingDesktopAtom, + XA_CARDINAL, 32, PropModeReplace, + (unsigned char *) &data, 1); +} + +void +leaveShowDesktopMode (CompScreen *s) +{ + CompWindow *w; + unsigned long data = 0; + + s->showingDesktopMask = 0; + + for (w = s->windows; w; w = w->next) + showWindow (w); + + XChangeProperty (s->display->display, s->root, + s->display->showingDesktopAtom, + XA_CARDINAL, 32, PropModeReplace, + (unsigned char *) &data, 1); +} + +void +sendWindowActivationRequest (CompScreen *s, + Window id) +{ + XEvent xev; + + xev.xclient.type = ClientMessage; + xev.xclient.display = s->display->display; + xev.xclient.format = 32; + + xev.xclient.message_type = s->display->winActiveAtom; + xev.xclient.window = id; + + xev.xclient.data.l[0] = 2; + xev.xclient.data.l[1] = 0; + xev.xclient.data.l[2] = 0; + xev.xclient.data.l[3] = 0; + xev.xclient.data.l[4] = 0; + + XSendEvent (s->display->display, + s->root, + FALSE, + SubstructureRedirectMask | SubstructureNotifyMask, + &xev); +} diff --git a/src/texture.c b/src/texture.c new file mode 100644 index 00000000..02e680a2 --- /dev/null +++ b/src/texture.c @@ -0,0 +1,337 @@ +/* + * Copyright © 2005 Novell, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without + * fee, provided that the above copyright notice appear in all copies + * and that both that copyright notice and this permission notice + * appear in supporting documentation, and that the name of + * Novell, Inc. not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior permission. + * Novell, Inc. makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + * + * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN + * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Author: David Reveman <davidr@novell.com> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <compiz.h> + +static CompMatrix _identity_matrix = { + 1.0f, 0.0f, + 0.0f, 1.0f, + 0.0f, 0.0f +}; + +void +initTexture (CompScreen *screen, + CompTexture *texture) +{ + texture->name = 0; + texture->target = GL_TEXTURE_2D; + texture->pixmap = None; + texture->filter = COMP_TEXTURE_FILTER_FAST; + texture->wrap = GL_CLAMP_TO_EDGE; + texture->matrix = _identity_matrix; +} + +void +finiTexture (CompScreen *screen, + CompTexture *texture) +{ + if (texture->name) + { + releasePixmapFromTexture (screen, texture); + glDeleteTextures (1, &texture->name); + } +} + +static Bool +imageToTexture (CompScreen *screen, + CompTexture *texture, + char *image, + unsigned int width, + unsigned int height) +{ + char *data; + int i; + + data = malloc (4 * width * height); + if (!data) + return FALSE; + + for (i = 0; i < height; i++) + memcpy (&data[i * width * 4], + &image[(height - i - 1) * width * 4], + width * 4); + + releasePixmapFromTexture (screen, texture); + + if (screen->textureNonPowerOfTwo || + (POWER_OF_TWO (width) && POWER_OF_TWO (height))) + { + texture->target = GL_TEXTURE_2D; + texture->matrix.xx = 1.0f / width; + texture->matrix.yy = -1.0f / height; + texture->matrix.y0 = 1.0f; + } + else + { + texture->target = GL_TEXTURE_RECTANGLE_NV; + texture->matrix.xx = 1.0f; + texture->matrix.yy = -1.0f; + texture->matrix.y0 = height; + } + + if (!texture->name) + glGenTextures (1, &texture->name); + + glBindTexture (texture->target, texture->name); + + glTexImage2D (texture->target, 0, GL_RGBA, width, height, 0, GL_BGRA, + +#if IMAGE_BYTE_ORDER == MSBFirst + GL_UNSIGNED_INT_8_8_8_8_REV, +#else + GL_UNSIGNED_BYTE, +#endif + + data); + + texture->filter = COMP_TEXTURE_FILTER_FAST; + + glTexParameteri (texture->target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri (texture->target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + glTexParameteri (texture->target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri (texture->target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + texture->wrap = GL_CLAMP_TO_EDGE; + + glBindTexture (texture->target, 0); + + free (data); + + return TRUE; +} + +Bool +readImageToTexture (CompScreen *screen, + CompTexture *texture, + char *imageFileName, + unsigned int *returnWidth, + unsigned int *returnHeight) +{ + char *image; + unsigned int width, height; + Bool status; + + if (!readPng (imageFileName, &image, &width, &height)) + { + fprintf (stderr, "%s: Failed to load image: %s\n", + programName, imageFileName); + return FALSE; + } + + status = imageToTexture (screen, texture, image, width, height); + + free (image); + + *returnWidth = width; + *returnHeight = height; + + return status; +} + +Bool +readImageBufferToTexture (CompScreen *screen, + CompTexture *texture, + const unsigned char *imageBuffer, + unsigned int *returnWidth, + unsigned int *returnHeight) +{ + char *image; + unsigned int width, height; + Bool status; + + if (!readPngBuffer (imageBuffer, &image, &width, &height)) + { + fprintf (stderr, "%s: Failed to load image buffer\n", programName); + return FALSE; + } + + status = imageToTexture (screen, texture, image, width, height); + + free (image); + + *returnWidth = width; + *returnHeight = height; + + return status; +} + +Bool +bindPixmapToTexture (CompScreen *screen, + CompTexture *texture, + Pixmap pixmap, + int width, + int height, + int depth) +{ + XVisualInfo *visinfo; + unsigned int target; + + visinfo = screen->glxPixmapVisuals[depth]; + if (!visinfo) + { + fprintf (stderr, "%s: No GL visual for depth %d\n", + programName, depth); + + return FALSE; + } + + texture->pixmap = glXCreateGLXPixmap (screen->display->display, + visinfo, pixmap); + if (!texture->pixmap) + { + fprintf (stderr, "%s: glXCreateGLXPixmap failed\n", programName); + + return FALSE; + } + + screen->queryDrawable (screen->display->display, + texture->pixmap, + GLX_TEXTURE_TARGET_EXT, + &target); + switch (target) { + case GLX_TEXTURE_2D_EXT: + texture->target = GL_TEXTURE_2D; + texture->matrix.xx = 1.0f / width; + texture->matrix.yy = -1.0f / height; + texture->matrix.y0 = 1.0f; + break; + case GLX_TEXTURE_RECTANGLE_EXT: + texture->target = GL_TEXTURE_RECTANGLE_ARB; + texture->matrix.xx = 1.0f; + texture->matrix.yy = -1.0f; + texture->matrix.y0 = height; + break; + case GLX_NO_TEXTURE_EXT: + fprintf (stderr, "%s: pixmap 0x%x can't be bound to texture\n", + programName, (int) pixmap); + + /* fall-through */ + default: + glXDestroyGLXPixmap (screen->display->display, texture->pixmap); + texture->pixmap = None; + + return FALSE; + } + + if (!texture->name) + glGenTextures (1, &texture->name); + + glBindTexture (texture->target, texture->name); + + if (!screen->bindTexImage (screen->display->display, + texture->pixmap, + GLX_FRONT_LEFT_EXT)) + { + fprintf (stderr, "%s: glXBindTexImage failed\n", programName); + + glXDestroyGLXPixmap (screen->display->display, texture->pixmap); + texture->pixmap = None; + + return FALSE; + } + + texture->filter = COMP_TEXTURE_FILTER_FAST; + + glTexParameteri (texture->target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri (texture->target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + glTexParameteri (texture->target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri (texture->target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + texture->wrap = GL_CLAMP_TO_EDGE; + + glBindTexture (texture->target, 0); + + return TRUE; +} + +void +releasePixmapFromTexture (CompScreen *screen, + CompTexture *texture) +{ + if (texture->pixmap) + { + if (!testMode) + { + glEnable (texture->target); + glBindTexture (texture->target, texture->name); + + screen->releaseTexImage (screen->display->display, + texture->pixmap, + GLX_FRONT_LEFT_EXT); + + glBindTexture (texture->target, 0); + glDisable (texture->target); + + glXDestroyGLXPixmap (screen->display->display, texture->pixmap); + } + texture->pixmap = None; + } +} + +void +enableTexture (CompScreen *screen, + CompTexture *texture, + CompTextureFilter filter) +{ + glEnable (texture->target); + glBindTexture (texture->target, texture->name); + + if (filter != texture->filter) + { + switch (filter) { + case COMP_TEXTURE_FILTER_FAST: + glTexParameteri (texture->target, + GL_TEXTURE_MIN_FILTER, + GL_NEAREST); + glTexParameteri (texture->target, + GL_TEXTURE_MAG_FILTER, + GL_NEAREST); + break; + case COMP_TEXTURE_FILTER_GOOD: + glTexParameteri (texture->target, + GL_TEXTURE_MIN_FILTER, + screen->display->textureFilter); + glTexParameteri (texture->target, + GL_TEXTURE_MAG_FILTER, + screen->display->textureFilter); + break; + } + + texture->filter = filter; + } +} + +void +disableTexture (CompTexture *texture) +{ + glBindTexture (texture->target, 0); + glDisable (texture->target); +} diff --git a/src/window.c b/src/window.c new file mode 100644 index 00000000..fc19390d --- /dev/null +++ b/src/window.c @@ -0,0 +1,2903 @@ +/* + * Copyright © 2005 Novell, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without + * fee, provided that the above copyright notice appear in all copies + * and that both that copyright notice and this permission notice + * appear in supporting documentation, and that the name of + * Novell, Inc. not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior permission. + * Novell, Inc. makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + * + * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN + * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Author: David Reveman <davidr@novell.com> + */ + +#include <X11/Xlib.h> +#include <X11/Xatom.h> +#include <X11/Xproto.h> +#include <X11/extensions/shape.h> +#include <X11/extensions/Xcomposite.h> + +#include <stdio.h> +#include <string.h> +#include <strings.h> +#include <stdlib.h> +#include <stdint.h> + +#include <compiz.h> + +#define MwmHintsDecorations (1L << 1) + +#define PropMotifWmHintElements 3 + +typedef struct { + unsigned long flags; + unsigned long functions; + unsigned long decorations; +} MwmHints; + +static int +reallocWindowPrivates (int size, + void *closure) +{ + CompScreen *s = (CompScreen *) closure; + CompWindow *w; + void *privates; + + for (w = s->windows; w; w = w->next) + { + privates = realloc (w->privates, size * sizeof (CompPrivate)); + if (!privates) + return FALSE; + + w->privates = (CompPrivate *) privates; + } + + return TRUE; +} + +int +allocateWindowPrivateIndex (CompScreen *screen) +{ + return allocatePrivateIndex (&screen->windowPrivateLen, + &screen->windowPrivateIndices, + reallocWindowPrivates, + (void *) screen); +} + +void +freeWindowPrivateIndex (CompScreen *screen, + int index) +{ + freePrivateIndex (screen->windowPrivateLen, + screen->windowPrivateIndices, + index); +} + +static void +recalcNormalHints (CompWindow *window) +{ + window->sizeHints.x = window->attrib.x; + window->sizeHints.y = window->attrib.y; + window->sizeHints.width = window->attrib.width; + window->sizeHints.height = window->attrib.height; + + if (window->sizeHints.flags & PMinSize) + { + window->sizeHints.base_width = window->sizeHints.min_width; + window->sizeHints.base_height = window->sizeHints.min_height; + } + else + { + window->sizeHints.base_width = 0; + window->sizeHints.base_height = 0; + } + + window->sizeHints.flags |= PBaseSize; + + if (window->sizeHints.flags & PBaseSize) + { + window->sizeHints.min_width = window->sizeHints.base_width; + window->sizeHints.min_height = window->sizeHints.base_height; + } + else + { + window->sizeHints.min_width = 0; + window->sizeHints.min_height = 0; + } + window->sizeHints.flags |= PMinSize; + + if (!(window->sizeHints.flags & PMaxSize)) + { + window->sizeHints.max_width = 65535; + window->sizeHints.max_height = 65535; + window->sizeHints.flags |= PMaxSize; + } + + if (window->sizeHints.max_width < window->sizeHints.min_width) + window->sizeHints.max_width = window->sizeHints.min_width; + + if (window->sizeHints.max_height < window->sizeHints.min_height) + window->sizeHints.max_height = window->sizeHints.min_height; + + if (window->sizeHints.min_width < 1) + window->sizeHints.min_width = 1; + + if (window->sizeHints.max_width < 1) + window->sizeHints.max_width = 1; + + if (window->sizeHints.min_height < 1) + window->sizeHints.min_height = 1; + + if (window->sizeHints.max_height < 1) + window->sizeHints.max_height = 1; + + if (window->sizeHints.flags & PResizeInc) + { + if (window->sizeHints.width_inc == 0) + window->sizeHints.width_inc = 1; + + if (window->sizeHints.height_inc == 0) + window->sizeHints.height_inc = 1; + } + else + { + window->sizeHints.width_inc = 1; + window->sizeHints.height_inc = 1; + window->sizeHints.flags |= PResizeInc; + } + + if (window->sizeHints.flags & PAspect) + { + /* don't divide by 0 */ + if (window->sizeHints.min_aspect.y < 1) + window->sizeHints.min_aspect.y = 1; + + if (window->sizeHints.max_aspect.y < 1) + window->sizeHints.max_aspect.y = 1; + } + else + { + window->sizeHints.min_aspect.x = 1; + window->sizeHints.min_aspect.y = 65535; + window->sizeHints.max_aspect.x = 65535; + window->sizeHints.max_aspect.y = 1; + window->sizeHints.flags |= PAspect; + } + + if (!(window->sizeHints.flags & PWinGravity)) + { + window->sizeHints.win_gravity = NorthWestGravity; + window->sizeHints.flags |= PWinGravity; + } +} + +void +updateNormalHints (CompWindow *w) +{ + Status status; + long supplied; + + status = XGetWMNormalHints (w->screen->display->display, w->id, + &w->sizeHints, &supplied); + + if (!status) + w->sizeHints.flags = 0; + + recalcNormalHints (w); +} + +void +updateWmHints (CompWindow *w) +{ + XWMHints *hints; + + hints = XGetWMHints (w->screen->display->display, w->id); + if (hints) + { + if (hints->flags & InputHint) + w->inputHint = hints->input; + + XFree (hints); + } +} + +void +updateWindowClassHints (CompWindow *w) +{ + XClassHint classHint; + int status; + + status = XGetClassHint (w->screen->display->display, w->id, &classHint); + + if (status) + { + if (classHint.res_name) + { + if (w->resName) + free (w->resName); + + w->resName = strdup (classHint.res_name); + XFree (classHint.res_name); + } + + if (classHint.res_class) + { + if (w->resClass) + free (w->resClass); + + w->resClass = strdup (classHint.res_class); + XFree (classHint.res_class); + } + } +} + +static Window +getClientLeaderOfAncestor (CompWindow *w) +{ + if (w->transientFor) + { + w = findWindowAtScreen (w->screen, w->transientFor); + if (w) + { + if (w->clientLeader) + return w->clientLeader; + + return getClientLeaderOfAncestor (w); + } + } + + return None; +} + +Window +getClientLeader (CompWindow *w) +{ + Atom actual; + int result, format; + unsigned long n, left; + unsigned char *data; + + result = XGetWindowProperty (w->screen->display->display, w->id, + w->screen->display->wmClientLeaderAtom, + 0L, 1L, False, XA_WINDOW, &actual, &format, + &n, &left, &data); + + if (result == Success && n && data) + { + Window win; + + memcpy (&win, data, sizeof (Window)); + XFree ((void *) data); + + if (win) + return win; + } + + return getClientLeaderOfAncestor (w); +} + +int +getWmState (CompDisplay *display, + Window id) +{ + Atom actual; + int result, format; + unsigned long n, left; + unsigned char *data; + unsigned long state = NormalState; + + result = XGetWindowProperty (display->display, id, + display->wmStateAtom, 0L, 2L, FALSE, + display->wmStateAtom, &actual, &format, + &n, &left, &data); + + if (result == Success && n && data) + { + memcpy (&state, data, sizeof (unsigned long)); + XFree ((void *) data); + } + + return state; +} + +void +setWmState (CompDisplay *display, + int state, + Window id) +{ + unsigned long data[2]; + + data[0] = state; + data[1] = None; + + XChangeProperty (display->display, id, + display->wmStateAtom, display->wmStateAtom, + 32, PropModeReplace, (unsigned char *) data, 2); +} + +unsigned int +windowStateMask (CompDisplay *display, + Atom state) +{ + if (state == display->winStateModalAtom) + return CompWindowStateModalMask; + else if (state == display->winStateStickyAtom) + return CompWindowStateStickyMask; + else if (state == display->winStateMaximizedVertAtom) + return CompWindowStateMaximizedVertMask; + else if (state == display->winStateMaximizedHorzAtom) + return CompWindowStateMaximizedHorzMask; + else if (state == display->winStateShadedAtom) + return CompWindowStateShadedMask; + else if (state == display->winStateSkipTaskbarAtom) + return CompWindowStateSkipTaskbarMask; + else if (state == display->winStateSkipPagerAtom) + return CompWindowStateSkipPagerMask; + else if (state == display->winStateHiddenAtom) + return CompWindowStateHiddenMask; + else if (state == display->winStateFullscreenAtom) + return CompWindowStateFullscreenMask; + else if (state == display->winStateAboveAtom) + return CompWindowStateAboveMask; + else if (state == display->winStateBelowAtom) + return CompWindowStateBelowMask; + else if (state == display->winStateDemandsAttentionAtom) + return CompWindowStateDemandsAttentationMask; + else if (state == display->winStateDisplayModalAtom) + return CompWindowStateDisplayModalMask; + + return 0; +} + +unsigned int +getWindowState (CompDisplay *display, + Window id) +{ + Atom actual; + int result, format; + unsigned long n, left; + unsigned char *data; + unsigned int state = 0; + + result = XGetWindowProperty (display->display, id, display->winStateAtom, + 0L, 1024L, FALSE, XA_ATOM, &actual, &format, + &n, &left, &data); + + if (result == Success && n && data) + { + Atom *a = (Atom *) data; + + while (n--) + state |= windowStateMask (display, *a++); + + XFree ((void *) data); + } + + return state; +} + +void +setWindowState (CompDisplay *display, + unsigned int state, + Window id) +{ + Atom data[32]; + int i = 0; + + if (state & CompWindowStateModalMask) + data[i++] = display->winStateModalAtom; + if (state & CompWindowStateStickyMask) + data[i++] = display->winStateStickyAtom; + if (state & CompWindowStateMaximizedVertMask) + data[i++] = display->winStateMaximizedVertAtom; + if (state & CompWindowStateMaximizedHorzMask) + data[i++] = display->winStateMaximizedHorzAtom; + if (state & CompWindowStateShadedMask) + data[i++] = display->winStateShadedAtom; + if (state & CompWindowStateSkipTaskbarMask) + data[i++] = display->winStateSkipTaskbarAtom; + if (state & CompWindowStateSkipPagerMask) + data[i++] = display->winStateSkipPagerAtom; + if (state & CompWindowStateHiddenMask) + data[i++] = display->winStateHiddenAtom; + if (state & CompWindowStateFullscreenMask) + data[i++] = display->winStateFullscreenAtom; + if (state & CompWindowStateAboveMask) + data[i++] = display->winStateAboveAtom; + if (state & CompWindowStateBelowMask) + data[i++] = display->winStateBelowAtom; + if (state & CompWindowStateDemandsAttentationMask) + data[i++] = display->winStateDemandsAttentionAtom; + if (state & CompWindowStateDisplayModalMask) + data[i++] = display->winStateDisplayModalAtom; + + XChangeProperty (display->display, id, display->winStateAtom, + XA_ATOM, 32, PropModeReplace, + (unsigned char *) data, i); +} + +static void +setWindowActions (CompDisplay *display, + unsigned int actions, + Window id) +{ + Atom data[32]; + int i = 0; + + if (actions & CompWindowActionMoveMask) + data[i++] = display->winActionMoveAtom; + if (actions & CompWindowActionResizeMask) + data[i++] = display->winActionResizeAtom; + if (actions & CompWindowActionStickMask) + data[i++] = display->winActionStickAtom; + if (actions & CompWindowActionMinimizeMask) + data[i++] = display->winActionMinimizeAtom; + if (actions & CompWindowActionMaximizeHorzMask) + data[i++] = display->winActionMaximizeHorzAtom; + if (actions & CompWindowActionMaximizeVertMask) + data[i++] = display->winActionMaximizeVertAtom; + if (actions & CompWindowActionFullscreenMask) + data[i++] = display->winActionFullscreenAtom; + if (actions & CompWindowActionCloseMask) + data[i++] = display->winActionCloseAtom; + + XChangeProperty (display->display, id, display->wmAllowedActionsAtom, + XA_ATOM, 32, PropModeReplace, + (unsigned char *) data, i); +} + +static void +recalcWindowActions (CompWindow *w) +{ + unsigned int actions = 0; + + switch (w->type) { + case CompWindowTypeFullscreenMask: + case CompWindowTypeNormalMask: + actions |= + CompWindowActionMinimizeMask | + CompWindowActionMaximizeHorzMask | + CompWindowActionMaximizeVertMask | + CompWindowActionFullscreenMask; + /* fall-through */ + case CompWindowTypeDialogMask: + case CompWindowTypeModalDialogMask: + actions |= + CompWindowActionMoveMask | + CompWindowActionResizeMask | + CompWindowActionStickMask | + CompWindowActionCloseMask; + break; + case CompWindowTypeUtilMask: + case CompWindowTypeToolbarMask: + case CompWindowTypeMenuMask: + case CompWindowTypeSplashMask: + case CompWindowTypeDesktopMask: + case CompWindowTypeDockMask: + case CompWindowTypeUnknownMask: + default: + break; + } + + if (actions != w->actions) + { + w->actions = actions; + setWindowActions (w->screen->display, actions, w->id); + } +} + +unsigned int +getWindowType (CompDisplay *display, + Window id) +{ + Atom actual; + int result, format; + unsigned long n, left; + unsigned char *data; + + result = XGetWindowProperty (display->display, id, display->winTypeAtom, + 0L, 1L, FALSE, XA_ATOM, &actual, &format, + &n, &left, &data); + + if (result == Success && n && data) + { + Atom a; + + memcpy (&a, data, sizeof (Atom)); + XFree ((void *) data); + + if (a == display->winTypeNormalAtom) + return CompWindowTypeNormalMask; + else if (a == display->winTypeMenuAtom) + return CompWindowTypeMenuMask; + else if (a == display->winTypeDesktopAtom) + return CompWindowTypeDesktopMask; + else if (a == display->winTypeDockAtom) + return CompWindowTypeDockMask; + else if (a == display->winTypeToolbarAtom) + return CompWindowTypeToolbarMask; + else if (a == display->winTypeUtilAtom) + return CompWindowTypeUtilMask; + else if (a == display->winTypeSplashAtom) + return CompWindowTypeSplashMask; + else if (a == display->winTypeDialogAtom) + return CompWindowTypeDialogMask; + } + + return CompWindowTypeUnknownMask; +} + +void +recalcWindowType (CompWindow *w) +{ + unsigned int type; + + type = w->wmType; + + if (!w->attrib.override_redirect && w->wmType == CompWindowTypeUnknownMask) + type = CompWindowTypeNormalMask; + + if (w->state & CompWindowStateFullscreenMask) + type = CompWindowTypeFullscreenMask; + + if (type == CompWindowTypeNormalMask) + { + if (w->transientFor) + type = CompWindowTypeDialogMask; + } + + if ((type & (CompWindowTypeNormalMask | CompWindowTypeDialogMask)) && + (w->state & CompWindowStateModalMask)) + type = CompWindowTypeModalDialogMask; + + if (type != w->type) + { + w->type = type; + recalcWindowActions (w); + } +} + +unsigned int +getMwmDecor (CompDisplay *display, + Window id) +{ + Atom actual; + int result, format; + unsigned long n, left; + MwmHints *mwmHints; + unsigned int decor = MwmDecorAll; + + result = XGetWindowProperty (display->display, id, display->mwmHintsAtom, + 0L, 20L, FALSE, display->mwmHintsAtom, + &actual, &format, &n, &left, + (unsigned char **) &mwmHints); + + if (result == Success && n && mwmHints) + { + if (n >= PropMotifWmHintElements) + { + if (mwmHints->flags & MwmHintsDecorations) + decor = mwmHints->decorations; + } + + XFree (mwmHints); + } + + return decor; +} + +unsigned int +getProtocols (CompDisplay *display, + Window id) +{ + Atom actual; + int result, format; + unsigned long n, left; + Atom *protocol; + unsigned int protocols = 0; + + result = XGetWindowProperty (display->display, id, display->wmProtocolsAtom, + 0L, 20L, FALSE, XA_ATOM, + &actual, &format, &n, &left, + (unsigned char **) &protocol); + + if (result == Success && n && protocol) + { + int i; + + for (i = 0; i < n; i++) + { + if (protocol[i] == display->wmDeleteWindowAtom) + protocols |= CompWindowProtocolDeleteMask; + else if (protocol[i] == display->wmTakeFocusAtom) + protocols |= CompWindowProtocolTakeFocusMask; + else if (protocol[i] == display->wmPingAtom) + protocols |= CompWindowProtocolPingMask; + else if (protocol[i] == display->wmSyncRequestAtom) + protocols |= CompWindowProtocolSyncRequestMask; + } + + XFree (protocol); + } + + return protocols; +} + +unsigned short +getWindowProp32 (CompDisplay *display, + Window id, + Atom property, + unsigned short defaultValue) +{ + Atom actual; + int result, format; + unsigned long n, left; + unsigned char *data; + + result = XGetWindowProperty (display->display, id, property, + 0L, 1L, FALSE, XA_CARDINAL, &actual, &format, + &n, &left, &data); + + if (result == Success && n && data) + { + CARD32 value; + + memcpy (&value, data, sizeof (CARD32)); + + XFree (data); + + return value >> 16; + } + + return defaultValue; +} + +void +setWindowProp32 (CompDisplay *display, + Window id, + Atom property, + unsigned short value) +{ + CARD32 value32; + + value32 = value << 16 | value; + + XChangeProperty (display->display, id, property, + XA_CARDINAL, 32, PropModeReplace, + (unsigned char *) &value32, 1); +} + + +static void +updateFrameWindow (CompWindow *w) +{ + if (w->input.left || w->input.right || w->input.top || w->input.bottom) + { + XRectangle rects[4]; + int x, y, width, height; + + x = w->serverX - w->input.left; + y = w->serverY - w->input.top; + width = w->width + w->input.left + w->input.right; + height = w->height + w->input.top + w->input.bottom; + + if (!w->frame) + { + XSetWindowAttributes attr; + XWindowChanges xwc; + + attr.event_mask = 0; + attr.override_redirect = TRUE; + + w->frame = XCreateWindow (w->screen->display->display, + w->screen->root, + x, y, width, height, 0, + CopyFromParent, + InputOnly, + CopyFromParent, + CWOverrideRedirect | CWEventMask, &attr); + + XGrabButton (w->screen->display->display, AnyButton, + AnyModifier, w->frame, TRUE, ButtonPressMask | + ButtonReleaseMask | ButtonMotionMask, + GrabModeSync, GrabModeSync, None, None); + + xwc.stack_mode = Below; + xwc.sibling = w->id; + + XConfigureWindow (w->screen->display->display, w->frame, + CWSibling | CWStackMode, &xwc); + + if (w->mapNum) + XMapWindow (w->screen->display->display, w->frame); + + XChangeProperty (w->screen->display->display, w->id, + w->screen->display->frameWindowAtom, + XA_WINDOW, 32, PropModeReplace, + (unsigned char *) &w->frame, 1); + } + + XResizeWindow (w->screen->display->display, w->frame, width, height); + + rects[0].x = 0; + rects[0].y = 0; + rects[0].width = width; + rects[0].height = w->input.top; + + rects[1].x = 0; + rects[1].y = w->input.top; + rects[1].width = w->input.left; + rects[1].height = height - w->input.top - w->input.bottom; + + rects[2].x = width - w->input.right; + rects[2].y = w->input.top; + rects[2].width = w->input.right; + rects[2].height = height - w->input.top - w->input.bottom; + + rects[3].x = 0; + rects[3].y = height - w->input.bottom; + rects[3].width = width; + rects[3].height = w->input.bottom; + + XShapeCombineRectangles (w->screen->display->display, + w->frame, + ShapeInput, + 0, + 0, + rects, + 4, + ShapeSet, + YXBanded); + } + else + { + if (w->frame) + { + XDestroyWindow (w->screen->display->display, w->frame); + w->frame = None; + } + } +} + +void +setWindowFrameExtents (CompWindow *w, + CompWindowExtents *input, + CompWindowExtents *output) +{ + if (input->left != w->input.left || + input->right != w->input.right || + input->top != w->input.top || + input->bottom != w->input.bottom) + { + unsigned long data[4]; + + w->input = *input; + + data[0] = input->left; + data[1] = input->right; + data[2] = input->top; + data[3] = input->bottom; + + updateFrameWindow (w); + + XChangeProperty (w->screen->display->display, w->id, + w->screen->display->frameExtentsAtom, + XA_CARDINAL, 32, PropModeReplace, + (unsigned char *) data, 4); + } + + if (output->left != w->output.left || + output->right != w->output.right || + output->top != w->output.top || + output->bottom != w->output.bottom) + { + w->output = *output; + + (*w->screen->windowResizeNotify) (w); + } +} + +static void +setWindowMatrix (CompWindow *w) +{ + w->matrix = w->texture.matrix; + w->matrix.x0 -= (w->attrib.x * w->matrix.xx); + w->matrix.y0 -= (w->attrib.y * w->matrix.yy); +} + +void +bindWindow (CompWindow *w) +{ + if (testMode) + { + unsigned int width, height; + + if (readImageToTexture (w->screen, &w->texture, + windowImage, &width, &height)) + { + XResizeWindow (w->screen->display->display, w->id, width, height); + + w->width = width; + w->height = height; + } + + w->pixmap = 1; + w->texture.pixmap = 1; + } + else + { + if (!w->pixmap) + { + w->pixmap = + XCompositeNameWindowPixmap (w->screen->display->display, + w->id); + if (!w->pixmap) + { + fprintf (stderr, "%s: XCompositeNameWindowPixmap failed\n", + programName); + } + } + + if (!bindPixmapToTexture (w->screen, &w->texture, w->pixmap, + w->width, w->height, + w->attrib.depth)) + { + fprintf (stderr, "%s: Couldn't bind redirected window 0x%x to " + "texture\n", programName, (int) w->id); + } + } + + setWindowMatrix (w); +} + +void +releaseWindow (CompWindow *w) +{ + if (w->pixmap) + { + releasePixmapFromTexture (w->screen, &w->texture); + + if (!testMode) + XFreePixmap (w->screen->display->display, w->pixmap); + + w->pixmap = None; + } +} + +static void +freeWindow (CompWindow *w) +{ + releaseWindow (w); + + if (w->syncAlarm) + XSyncDestroyAlarm (w->screen->display->display, w->syncAlarm); + + if (w->syncWaitHandle) + compRemoveTimeout (w->syncWaitHandle); + + if (w->texture.name) + finiTexture (w->screen, &w->texture); + + if (w->clip) + XDestroyRegion (w->clip); + + if (w->region) + XDestroyRegion (w->region); + + if (w->privates) + free (w->privates); + + if (w->sizeDamage) + free (w->damageRects); + + if (w->vertices) + free (w->vertices); + + if (w->indices) + free (w->indices); + + if (lastFoundWindow == w) + lastFoundWindow = 0; + + if (lastDamagedWindow == w) + lastDamagedWindow = 0; + + if (w->struts) + free (w->struts); + + if (w->startupId) + free (w->startupId); + + if (w->resName) + free (w->resName); + + if (w->resClass) + free (w->resClass); + + free (w); +} + +void +damageWindowRegion (CompWindow *w, + Region region) +{ + if (w->scaled) + { + REGION reg; + int x1, y1, x2, y2; + + reg.rects = ®.extents; + reg.numRects = 1; + + x1 = region->extents.x1 - w->attrib.x; + y1 = region->extents.y1 - w->attrib.y; + x2 = region->extents.x2 - w->attrib.x; + y2 = region->extents.y2 - w->attrib.y; + + reg.extents.x1 = (x1 * w->paint.xScale) + w->attrib.x; + reg.extents.y1 = (y1 * w->paint.yScale) + w->attrib.y; + reg.extents.x2 = (x2 * w->paint.xScale + 0.5f) + w->attrib.x; + reg.extents.y2 = (y2 * w->paint.yScale + 0.5f) + w->attrib.y; + + if (reg.extents.x2 > reg.extents.x1 && reg.extents.y2 > reg.extents.y1) + damageScreenRegion (w->screen, ®); + } + else + { + damageScreenRegion (w->screen, region); + } +} + +void +damageWindowOutputExtents (CompWindow *w) +{ + if (w->screen->damageMask & COMP_SCREEN_DAMAGE_ALL_MASK) + return; + + if (w->attrib.map_state == IsViewable && w->damaged) + { + REGION reg; + + reg.rects = ®.extents; + reg.numRects = reg.size = 1; + + /* top */ + reg.extents.x1 = w->attrib.x - w->output.left; + reg.extents.y1 = w->attrib.y - w->output.top; + reg.extents.x2 = w->attrib.x + w->width + w->output.right; + reg.extents.y2 = w->attrib.y; + + if (reg.extents.x1 < reg.extents.x2 && reg.extents.y1 < reg.extents.y2) + damageWindowRegion (w, ®); + + /* bottom */ + reg.extents.y1 = w->attrib.y + w->height; + reg.extents.y2 = reg.extents.y1 + w->output.bottom; + + if (reg.extents.x1 < reg.extents.x2 && reg.extents.y1 < reg.extents.y2) + damageWindowRegion (w, ®); + + /* left */ + reg.extents.x1 = w->attrib.x - w->output.left; + reg.extents.y1 = w->attrib.y; + reg.extents.x2 = w->attrib.x; + reg.extents.y2 = w->attrib.y + w->height; + + if (reg.extents.x1 < reg.extents.x2 && reg.extents.y1 < reg.extents.y2) + damageWindowRegion (w, ®); + + /* right */ + reg.extents.x1 = w->attrib.x + w->width; + reg.extents.x2 = reg.extents.x1 + w->output.right; + + if (reg.extents.x1 < reg.extents.x2 && reg.extents.y1 < reg.extents.y2) + damageWindowRegion (w, ®); + } +} + +Bool +damageWindowRect (CompWindow *w, + Bool initial, + BoxPtr rect) +{ + return FALSE; +} + +void +addWindowDamage (CompWindow *w) +{ + if (w->screen->damageMask & COMP_SCREEN_DAMAGE_ALL_MASK) + return; + + if (w->attrib.map_state == IsViewable && w->damaged) + { + REGION region; + + region.extents.x1 = -w->output.left - w->attrib.border_width; + region.extents.y1 = -w->output.top - w->attrib.border_width; + region.extents.x2 = w->width + w->output.right; + region.extents.y2 = w->height + w->output.bottom; + + if (!(*w->screen->damageWindowRect) (w, FALSE, ®ion.extents)) + { + region.extents.x1 += w->attrib.x + w->attrib.border_width; + region.extents.y1 += w->attrib.y + w->attrib.border_width; + region.extents.x2 += w->attrib.x + w->attrib.border_width; + region.extents.y2 += w->attrib.y + w->attrib.border_width; + + region.rects = ®ion.extents; + region.numRects = region.size = 1; + + damageWindowRegion (w, ®ion); + } + } +} + +void +updateWindowRegion (CompWindow *w) +{ + REGION rect; + XRectangle r, *rects, *shapeRects = 0; + int i, n = 0; + + EMPTY_REGION (w->region); + + if (w->screen->display->shapeExtension) + { + int order; + + shapeRects = XShapeGetRectangles (w->screen->display->display, w->id, + ShapeBounding, &n, &order); + } + + if (n < 2) + { + r.x = -w->attrib.border_width; + r.y = -w->attrib.border_width; + r.width = w->width; + r.height = w->height; + + rects = &r; + n = 1; + } + else + { + rects = shapeRects; + } + + rect.rects = &rect.extents; + rect.numRects = rect.size = 1; + + for (i = 0; i < n; i++) + { + rect.extents.x1 = rects[i].x + w->attrib.border_width; + rect.extents.y1 = rects[i].y + w->attrib.border_width; + rect.extents.x2 = rect.extents.x1 + rects[i].width; + rect.extents.y2 = rect.extents.y1 + rects[i].height; + + if (rect.extents.x1 < 0) + rect.extents.x1 = 0; + if (rect.extents.y1 < 0) + rect.extents.y1 = 0; + if (rect.extents.x2 > w->width) + rect.extents.x2 = w->width; + if (rect.extents.y2 > w->height) + rect.extents.y2 = w->height; + + if (rect.extents.y1 < rect.extents.y2 && + rect.extents.x1 < rect.extents.x2) + { + rect.extents.x1 += w->attrib.x; + rect.extents.y1 += w->attrib.y; + rect.extents.x2 += w->attrib.x; + rect.extents.y2 += w->attrib.y; + + XUnionRegion (&rect, w->region, w->region); + } + } + + if (shapeRects) + XFree (shapeRects); +} + +Bool +updateWindowStruts (CompWindow *w) +{ + Atom actual; + int result, format; + unsigned long n, left; + unsigned long *struts = NULL; + Bool hasOld, hasNew; + CompStruts old, new; + +#define MIN_EMPTY 76 + + if (w->struts) + { + hasOld = TRUE; + + old.left = w->struts->left; + old.right = w->struts->right; + old.top = w->struts->top; + old.bottom = w->struts->bottom; + } + else + { + hasOld = FALSE; + } + + hasNew = FALSE; + + new.left.x = 0; + new.left.y = 0; + new.left.width = 0; + new.left.height = w->screen->height; + + new.right.x = w->screen->width; + new.right.y = 0; + new.right.width = 0; + new.right.height = w->screen->height; + + new.top.x = 0; + new.top.y = 0; + new.top.width = w->screen->width; + new.top.height = 0; + + new.bottom.x = 0; + new.bottom.y = w->screen->height; + new.bottom.width = w->screen->width; + new.bottom.height = 0; + + result = XGetWindowProperty (w->screen->display->display, w->id, + w->screen->display->wmStrutPartialAtom, + 0L, 12L, FALSE, XA_CARDINAL, &actual, &format, + &n, &left, (unsigned char **) &struts); + + if (result == Success && n && struts) + { + if (n == 12) + { + int gap; + + hasNew = TRUE; + + gap = w->screen->width - struts[0] - struts[1]; + gap -= MIN_EMPTY; + + new.left.width = (int) struts[0] + MIN (0, gap / 2); + new.right.width = (int) struts[1] + MIN (0, gap / 2); + + gap = w->screen->height - struts[2] - struts[3]; + gap -= MIN_EMPTY; + + new.top.height = (int) struts[2] + MIN (0, gap / 2); + new.bottom.height = (int) struts[3] + MIN (0, gap / 2); + + new.right.x = w->screen->width - new.right.width; + new.bottom.y = w->screen->height - new.bottom.height; + + new.left.y = struts[4]; + new.left.height = struts[5] - new.left.y + 1; + new.right.y = struts[6]; + new.right.height = struts[7] - new.right.y + 1; + + new.top.x = struts[8]; + new.top.width = struts[9] - new.top.x + 1; + new.bottom.x = struts[10]; + new.bottom.width = struts[11] - new.bottom.x + 1; + } + + XFree (struts); + } + + if (!hasNew) + { + result = XGetWindowProperty (w->screen->display->display, w->id, + w->screen->display->wmStrutAtom, + 0L, 4L, FALSE, XA_CARDINAL, + &actual, &format, &n, &left, + (unsigned char **) &struts); + + if (result == Success && n && struts) + { + if (n == 4) + { + int gap; + + hasNew = TRUE; + + gap = w->screen->width - struts[0] - struts[1]; + gap -= MIN_EMPTY; + + new.left.width = (int) struts[0] + MIN (0, gap / 2); + new.right.width = (int) struts[1] + MIN (0, gap / 2); + + gap = w->screen->height - struts[2] - struts[3]; + gap -= MIN_EMPTY; + + new.top.height = (int) struts[2] + MIN (0, gap / 2); + new.bottom.height = (int) struts[3] + MIN (0, gap / 2); + + new.left.x = 0; + new.right.x = w->screen->width - new.right.width; + + new.top.y = 0; + new.bottom.y = w->screen->height - new.bottom.height; + } + + XFree (struts); + } + } + + if (hasOld != hasNew || (hasNew && hasOld && + memcmp (&new, &old, sizeof (CompStruts)))) + { + if (hasNew) + { + if (!w->struts) + w->struts = malloc (sizeof (CompStruts)); + + *w->struts = new; + } + else + { + free (w->struts); + w->struts = NULL; + } + + return TRUE; + } + + return FALSE; +} + +void +addWindow (CompScreen *screen, + Window id, + Window aboveId) +{ + CompWindow *w; + + w = (CompWindow *) malloc (sizeof (CompWindow)); + if (!w) + return; + + w->next = NULL; + w->prev = NULL; + + w->mapNum = 0; + w->activeNum = 0; + + w->frame = None; + + w->placed = FALSE; + w->minimized = FALSE; + + w->startupId = NULL; + w->resName = NULL; + w->resClass = NULL; + + initTexture (screen, &w->texture); + + w->screen = screen; + w->pixmap = None; + w->destroyed = FALSE; + w->damaged = FALSE; + + w->destroyRefCnt = 1; + w->unmapRefCnt = 1; + + w->group = NULL; + + w->damageRects = 0; + w->sizeDamage = 0; + w->nDamage = 0; + + w->vertices = 0; + w->vertexSize = 0; + w->indices = 0; + w->indexSize = 0; + w->vCount = 0; + + w->struts = 0; + + w->input.left = 0; + w->input.right = 0; + w->input.top = 0; + w->input.bottom = 0; + + w->output.left = 0; + w->output.right = 0; + w->output.top = 0; + w->output.bottom = 0; + + w->paint.opacity = OPAQUE; + w->paint.brightness = 0xffff; + w->paint.saturation = COLOR; + w->paint.xScale = 1.0f; + w->paint.yScale = 1.0f; + + w->alive = TRUE; + w->saturation = COLOR; + + w->scaled = FALSE; + + w->mwmDecor = MwmDecorAll; + + if (screen->windowPrivateLen) + { + w->privates = malloc (screen->windowPrivateLen * sizeof (CompPrivate)); + if (!w->privates) + { + free (w); + return; + } + } + else + w->privates = 0; + + w->region = XCreateRegion (); + if (!w->region) + { + freeWindow (w); + return; + } + + w->clip = XCreateRegion (); + if (!w->clip) + { + freeWindow (w); + return; + } + + if (!XGetWindowAttributes (screen->display->display, id, &w->attrib)) + { + freeWindow (w); + return; + } + + w->width = w->attrib.width + w->attrib.border_width * 2; + w->height = w->attrib.height + w->attrib.border_width * 2; + + w->sizeHints.flags = 0; + + recalcNormalHints (w); + + w->transientFor = None; + w->clientLeader = None; + + w->serverX = w->attrib.x; + w->serverY = w->attrib.y; + + w->syncAlarm = None; + w->syncCounter = 0; + w->syncWaitHandle = 0; + + w->syncWait = FALSE; + w->syncX = w->attrib.x; + w->syncY = w->attrib.y; + w->syncWidth = w->attrib.width; + w->syncHeight = w->attrib.height; + w->syncBorderWidth = w->attrib.border_width; + + w->saveMask = 0; + + XSelectInput (screen->display->display, id, + PropertyChangeMask | + EnterWindowMask | + FocusChangeMask); + + w->id = id; + + XGrabButton (screen->display->display, AnyButton, + AnyModifier, w->id, TRUE, ButtonPressMask | + ButtonReleaseMask | ButtonMotionMask, + GrabModeSync, GrabModeSync, None, None); + + w->inputHint = TRUE; + w->alpha = (w->attrib.depth == 32); + w->wmType = 0; + w->state = 0; + w->actions = 0; + w->protocols = 0; + w->type = CompWindowTypeUnknownMask; + w->lastPong = screen->display->lastPing; + + if (screen->display->shapeExtension) + XShapeSelectInput (screen->display->display, id, ShapeNotifyMask); + + insertWindowIntoScreen (screen, w, aboveId); + + EMPTY_REGION (w->region); + + if (w->attrib.class != InputOnly) + { + REGION rect; + + rect.rects = &rect.extents; + rect.numRects = rect.size = 1; + + rect.extents.x1 = w->attrib.x; + rect.extents.y1 = w->attrib.y; + rect.extents.x2 = w->attrib.x + w->width; + rect.extents.y2 = w->attrib.y + w->height; + + XUnionRegion (&rect, w->region, w->region); + + initTexture (screen, &w->texture); + + w->damage = XDamageCreate (screen->display->display, id, + XDamageReportRawRectangles); + + /* need to check for DisplayModal state on all windows */ + w->state = getWindowState (w->screen->display, w->id); + + updateWindowClassHints (w); + } + else + { + w->damage = None; + w->attrib.map_state = IsUnmapped; + } + + if (testMode) + { + static int useAlpha = 0; + + w->attrib.map_state = IsViewable; + w->damaged = TRUE; + + w->attrib.width = 0; + w->attrib.height = 0; + + bindWindow (w); + + w->alpha = useAlpha; + useAlpha = !useAlpha; + } + + w->invisible = TRUE; + + if (!w->attrib.override_redirect) + { + updateNormalHints (w); + updateWindowStruts (w); + + updateWmHints (w); + + XGetTransientForHint (w->screen->display->display, + w->id, &w->transientFor); + + w->clientLeader = getClientLeader (w); + + w->wmType = getWindowType (w->screen->display, w->id); + + recalcWindowType (w); + + w->mwmDecor = getMwmDecor (w->screen->display, w->id); + w->protocols = getProtocols (w->screen->display, w->id); + + if (!(w->type & CompWindowTypeDesktopMask)) + w->paint.opacity = + getWindowProp32 (w->screen->display, w->id, + w->screen->display->winOpacityAtom, + OPAQUE); + + w->paint.brightness = + getWindowProp32 (w->screen->display, w->id, + w->screen->display->winBrightnessAtom, + BRIGHT); + + if (w->screen->canDoSaturated) + { + w->saturation = + getWindowProp32 (w->screen->display, w->id, + w->screen->display->winSaturationAtom, + COLOR); + if (w->alive) + w->paint.saturation = w->saturation; + } + } + + if (w->attrib.map_state == IsViewable) + { + w->attrib.map_state = IsUnmapped; + + mapWindow (w); + + if (!w->attrib.override_redirect) + updateWindowAttributes (w); + } + + windowInitPlugins (w); +} + +void +removeWindow (CompWindow *w) +{ + unhookWindowFromScreen (w->screen, w); + + if (w->attrib.map_state == IsViewable && w->damaged) + { + if (w->type == CompWindowTypeDesktopMask) + w->screen->desktopWindowCount--; + + if (w->struts) + updateWorkareaForScreen (w->screen); + + updateClientListForScreen (w->screen); + } + else if (w->state & CompWindowStateHiddenMask) + { + updateClientListForScreen (w->screen); + } + + windowFiniPlugins (w); + + freeWindow (w); +} + +void +destroyWindow (CompWindow *w) +{ + w->id = 1; + w->mapNum = 0; + + w->destroyRefCnt--; + if (w->destroyRefCnt) + return; + + if (!w->destroyed) + { + w->destroyed = TRUE; + w->screen->pendingDestroys++; + } +} + +static void +sendConfigureNotify (CompWindow *w) +{ + XConfigureEvent xev; + + xev.type = ConfigureNotify; + xev.event = w->id; + xev.window = w->id; + xev.x = w->serverX; + xev.y = w->serverY; + xev.width = w->attrib.width; + xev.height = w->attrib.height; + xev.border_width = w->attrib.border_width; + + xev.above = (w->prev) ? w->prev->id : None; + xev.override_redirect = w->attrib.override_redirect; + + XSendEvent (w->screen->display->display, w->id, FALSE, + StructureNotifyMask, (XEvent *) &xev); +} + +void +mapWindow (CompWindow *w) +{ + if (w->attrib.class == InputOnly) + return; + + if (w->attrib.map_state == IsViewable) + return; + + w->unmapRefCnt = 1; + + w->attrib.map_state = IsViewable; + + setWmState (w->screen->display, NormalState, w->id); + + w->invisible = TRUE; + w->damaged = FALSE; + w->alive = TRUE; + + w->lastPong = w->screen->display->lastPing; + + w->mapNum = w->screen->mapNum++; + + updateWindowRegion (w); + + if (w->frame) + XMapWindow (w->screen->display->display, w->frame); + + if (w->struts) + updateWorkareaForScreen (w->screen); + + updateClientListForScreen (w->screen); + + if (w->type & CompWindowTypeDesktopMask) + w->screen->desktopWindowCount++; + + if (w->protocols & CompWindowProtocolSyncRequestMask) + { + sendSyncRequest (w); + sendConfigureNotify (w); + } +} + +void +unmapWindow (CompWindow *w) +{ + w->mapNum = 0; + + if (w->frame) + XUnmapWindow (w->screen->display->display, w->frame); + + w->unmapRefCnt--; + if (w->unmapRefCnt > 0) + return; + + if (w->attrib.map_state != IsViewable) + return; + + if (w->type == CompWindowTypeDesktopMask) + w->screen->desktopWindowCount--; + + addWindowDamage (w); + + w->attrib.map_state = IsUnmapped; + + setWmState (w->screen->display, IconicState, w->id); + + w->invisible = TRUE; + + releaseWindow (w); + + if (w->struts) + updateWorkareaForScreen (w->screen); + + updateClientListForScreen (w->screen); +} + +static int +restackWindow (CompWindow *w, + Window aboveId) +{ + if (w->prev) + { + if (aboveId && aboveId == w->prev->id) + return 0; + } + else if (aboveId == None) + return 0; + + unhookWindowFromScreen (w->screen, w); + insertWindowIntoScreen (w->screen, w, aboveId); + + updateClientListForScreen (w->screen); + + return 1; +} + +Bool +resizeWindow (CompWindow *w, + int x, + int y, + int width, + int height, + int borderWidth) +{ + Window frame = None; + + if (x != w->serverX) + { + frame = w->frame; + w->serverX = x; + } + else + x = w->attrib.x; + + if (y != w->serverY) + { + frame = w->frame; + w->serverY = y; + } + else + y = w->attrib.y; + + if (frame) + XMoveWindow (w->screen->display->display, frame, + w->serverX - w->input.left, + w->serverY - w->input.top); + + if (w->attrib.width != width || + w->attrib.height != height || + w->attrib.border_width != borderWidth) + { + unsigned int pw, ph, actualWidth, actualHeight, ui; + Pixmap pixmap = None; + Window root; + Status result; + int i; + + pw = width + borderWidth * 2; + ph = height + borderWidth * 2; + + if (!w->invisible) + { + pixmap = XCompositeNameWindowPixmap (w->screen->display->display, + w->id); + if (!pixmap) + { + fprintf (stderr, "%s: XCompositeNameWindowPixmap failed\n", + programName); + + return FALSE; + } + + result = XGetGeometry (w->screen->display->display, pixmap, &root, + &i, &i, &actualWidth, &actualHeight, + &ui, &ui); + + if (actualWidth != pw || actualHeight != ph) + { + XFreePixmap (w->screen->display->display, pixmap); + + return FALSE; + } + } + + addWindowDamage (w); + + w->attrib.x = x; + w->attrib.y = y; + w->attrib.width = width; + w->attrib.height = height; + w->attrib.border_width = borderWidth; + + w->width = pw; + w->height = ph; + + releaseWindow (w); + + w->pixmap = pixmap; + + if (w->mapNum) + updateWindowRegion (w); + + (*w->screen->windowResizeNotify) (w); + + addWindowDamage (w); + + w->invisible = WINDOW_INVISIBLE (w); + + updateFrameWindow (w); + } + else if (w->attrib.x != x || w->attrib.y != y) + { + int dx, dy; + + dx = x - w->attrib.x; + dy = y - w->attrib.y; + + moveWindow (w, dx, dy, TRUE); + } + + return TRUE; +} + +static void +syncValueIncrement (XSyncValue *value) +{ + XSyncValue one; + int overflow; + + XSyncIntToValue (&one, 1); + XSyncValueAdd (value, *value, one, &overflow); +} + +static Bool +initializeSyncCounter (CompWindow *w) +{ + XSyncAlarmAttributes values; + Atom actual; + int result, format; + unsigned long n, left; + unsigned long *counter; + + if (w->syncCounter) + return w->syncAlarm != None; + + if (!(w->protocols & CompWindowProtocolSyncRequestMask)) + return FALSE; + + result = XGetWindowProperty (w->screen->display->display, w->id, + w->screen->display->wmSyncRequestCounterAtom, + 0L, 1L, FALSE, XA_CARDINAL, &actual, &format, + &n, &left, (unsigned char **) &counter); + + if (result == Success && n && counter) + { + w->syncCounter = *counter; + + XFree (counter); + + XSyncIntsToValue (&w->syncValue, (unsigned int) rand (), 0); + XSyncSetCounter (w->screen->display->display, + w->syncCounter, + w->syncValue); + + syncValueIncrement (&w->syncValue); + + values.events = TRUE; + + values.trigger.counter = w->syncCounter; + values.trigger.wait_value = w->syncValue; + + values.trigger.value_type = XSyncAbsolute; + values.trigger.test_type = XSyncPositiveComparison; + + XSyncIntToValue (&values.delta, 1); + + values.events = TRUE; + + compCheckForError (w->screen->display->display); + + /* Note that by default, the alarm increments the trigger value + * when it fires until the condition (counter.value < trigger.value) + * is FALSE again. + */ + w->syncAlarm = XSyncCreateAlarm (w->screen->display->display, + XSyncCACounter | + XSyncCAValue | + XSyncCAValueType | + XSyncCATestType | + XSyncCADelta | + XSyncCAEvents, + &values); + + if (!compCheckForError (w->screen->display->display)) + return TRUE; + + XSyncDestroyAlarm (w->screen->display->display, w->syncAlarm); + w->syncAlarm = None; + } + + return FALSE; +} + +static Bool +syncWaitTimeout (void *closure) +{ + CompWindow *w = closure; + + w->syncWaitHandle = 0; + handleSyncAlarm (w); + + return FALSE; +} + +void +sendSyncRequest (CompWindow *w) +{ + XClientMessageEvent xev; + + if (w->syncWait) + return; + + if (!initializeSyncCounter (w)) + return; + + xev.type = ClientMessage; + xev.window = w->id; + xev.message_type = w->screen->display->wmProtocolsAtom; + xev.format = 32; + xev.data.l[0] = w->screen->display->wmSyncRequestAtom; + xev.data.l[1] = CurrentTime; + xev.data.l[2] = XSyncValueLow32 (w->syncValue); + xev.data.l[3] = XSyncValueHigh32 (w->syncValue); + xev.data.l[4] = 0; + + syncValueIncrement (&w->syncValue); + + XSendEvent (w->screen->display->display, w->id, FALSE, 0, (XEvent *) &xev); + + w->syncWait = TRUE; + w->syncX = w->serverX; + w->syncY = w->serverY; + w->syncWidth = w->attrib.width; + w->syncHeight = w->attrib.height; + w->syncBorderWidth = w->attrib.border_width; + + if (!w->syncWaitHandle) + w->syncWaitHandle = compAddTimeout (1000, syncWaitTimeout, w); +} + +void +configureWindow (CompWindow *w, + XConfigureEvent *ce) +{ + if (w->syncWait) + { + w->syncX = ce->x; + w->syncY = ce->y; + w->syncWidth = ce->width; + w->syncHeight = ce->height; + w->syncBorderWidth = ce->border_width; + } + else + { + resizeWindow (w, ce->x, ce->y, ce->width, ce->height, + ce->border_width); + } + + w->attrib.override_redirect = ce->override_redirect; + + if (restackWindow (w, ce->above)) + addWindowDamage (w); +} + +void +circulateWindow (CompWindow *w, + XCirculateEvent *ce) +{ + Window newAboveId; + + if (ce->place == PlaceOnTop && w->screen->reverseWindows) + newAboveId = w->screen->reverseWindows->id; + else + newAboveId = 0; + + if (restackWindow (w, newAboveId)) + addWindowDamage (w); +} + +void +moveWindow (CompWindow *w, + int dx, + int dy, + Bool damage) +{ + if (dx || dy) + { + if (damage) + addWindowDamage (w); + + w->attrib.x += dx; + w->attrib.y += dy; + + XOffsetRegion (w->region, dx, dy); + + setWindowMatrix (w); + + w->invisible = WINDOW_INVISIBLE (w); + + (*w->screen->windowMoveNotify) (w, dx, dy); + + if (damage) + addWindowDamage (w); + } +} + +void +syncWindowPosition (CompWindow *w) +{ + if (w->attrib.x != w->serverX || w->attrib.y != w->serverY) + { + XMoveWindow (w->screen->display->display, w->id, + w->attrib.x, + w->attrib.y); + + if (0 && !w->attrib.override_redirect) + { + XConfigureEvent ce; + + ce.type = ConfigureNotify; + ce.display = w->screen->display->display; + ce.event = w->id; + ce.window = w->id; + ce.x = w->attrib.x; + ce.y = w->attrib.y; + ce.width = w->attrib.width; + ce.height = w->attrib.height; + ce.border_width = w->attrib.border_width; + ce.above = (w->prev) ? w->prev->id : None; + ce.override_redirect = FALSE; + + XSendEvent (w->screen->display->display, + w->id, FALSE, StructureNotifyMask, + (XEvent *) &ce); + } + } +} + +void +setWindowScale (CompWindow *w, + float xScale, + float yScale) +{ + if (xScale > 0.999f && xScale < 1.001f && + yScale > 0.999f && yScale < 1.001f) + { + w->paint.xScale = 1.0f; + w->paint.yScale = 1.0f; + + w->scaled = FALSE; + } + else + { + w->paint.xScale = xScale; + w->paint.yScale = yScale; + + w->scaled = TRUE; + } +} + +Bool +focusWindow (CompWindow *w) +{ + if (w->attrib.override_redirect) + return FALSE; + + if (!w->mapNum || w->attrib.map_state != IsViewable) + return FALSE; + + if (w->attrib.x + w->width <= 0 || + w->attrib.y + w->height <= 0 || + w->attrib.x >= w->screen->width || + w->attrib.y >= w->screen->height) + return FALSE; + + if (w->inputHint || (w->protocols & CompWindowProtocolTakeFocusMask)) + return TRUE; + + return FALSE; +} + +void +windowResizeNotify (CompWindow *w) +{ +} + +void +windowMoveNotify (CompWindow *w, + int dx, + int dy) +{ +} + +void +windowGrabNotify (CompWindow *w, + int x, + int y, + unsigned int state, + unsigned int mask) +{ +} + +void +windowUngrabNotify (CompWindow *w) +{ +} + +static Bool +isGroupTransient (CompWindow *w, + Window clientLeader) +{ + if (!clientLeader) + return FALSE; + + if (w->transientFor == None || w->transientFor == w->screen->root) + { + if (w->type & (CompWindowTypeDialogMask | + CompWindowTypeModalDialogMask)) + { + if (w->clientLeader == clientLeader) + return TRUE; + } + } + + return FALSE; +} + +static CompWindow * +getModalTransient (CompWindow *window) +{ + CompWindow *w, *modalTransient; + + modalTransient = window; + + for (w = window->screen->reverseWindows; w; w = w->prev) + { + if (w == modalTransient || w->mapNum == 0) + continue; + + if (w->transientFor == modalTransient->id) + { + if (w->state & CompWindowStateModalMask) + { + modalTransient = w; + w = window->screen->reverseWindows; + } + } + } + + if (modalTransient == window) + { + for (w = window->screen->reverseWindows; w; w = w->prev) + { + if (w == modalTransient || w->mapNum == 0) + continue; + + if (isGroupTransient (w, modalTransient->clientLeader)) + { + if (w->state & CompWindowStateModalMask) + { + modalTransient = w; + break; + } + } + } + } + + if (modalTransient == window) + modalTransient = NULL; + + return modalTransient; +} + +void +moveInputFocusToWindow (CompWindow *w) +{ + CompDisplay *d = w->screen->display; + CompWindow *modalTransient; + + modalTransient = getModalTransient (w); + if (modalTransient) + w = modalTransient; + + if (w->id != d->activeWindow) + { + if (w->inputHint || (w->protocols & CompWindowProtocolTakeFocusMask)) + XSetInputFocus (d->display, w->id, RevertToPointerRoot, + CurrentTime); + } +} + +static Bool +isAncestorTo (CompWindow *transient, + CompWindow *ancestor) +{ + if (transient->transientFor) + { + if (transient->transientFor == ancestor->id) + return TRUE; + + transient = findWindowAtScreen (transient->screen, + transient->transientFor); + if (transient) + return isAncestorTo (transient, ancestor); + } + + return FALSE; +} + + +static Bool +stackLayerCheck (CompWindow *w, + Window clientLeader, + CompWindow *below) +{ + if (w->transientFor == below->id) + return TRUE; + + if (isAncestorTo (below, w)) + return FALSE; + + if (clientLeader && below->clientLeader == clientLeader) + if (isGroupTransient (below, clientLeader)) + return FALSE; + + if (w->state & CompWindowStateAboveMask) + { + return TRUE; + } + else if (w->state & CompWindowStateBelowMask) + { + if (below->state & CompWindowStateBelowMask) + return TRUE; + } + else if (!(below->state & CompWindowStateAboveMask)) + { + return TRUE; + } + + return FALSE; +} + +/* goes through the stack, top-down until we find a window we should + stack above, normal windows can be stacked above fullscreen windows. */ +static CompWindow * +findSibilingBelow (CompWindow *w) +{ + CompWindow *below; + Window clientLeader = w->clientLeader; + unsigned int type = w->type; + + /* normal stacking fullscreen windows with below state */ + if ((type & CompWindowTypeFullscreenMask) && + (w->state & CompWindowStateBelowMask)) + type = CompWindowTypeNormalMask; + + if (w->transientFor || isGroupTransient (w, clientLeader)) + clientLeader = None; + + for (below = w->screen->reverseWindows; below; below = below->prev) + { + if (below == w) + continue; + + if (below->attrib.override_redirect) + continue; + + if (below->attrib.map_state != IsViewable || below->mapNum == 0) + continue; + + /* always above desktop windows */ + if (below->type & CompWindowTypeDesktopMask) + return below; + + switch (type) { + case CompWindowTypeDesktopMask: + /* desktop window layer */ + break; + case CompWindowTypeFullscreenMask: + case CompWindowTypeDockMask: + /* fullscreen and dock layer */ + if (below->type & (CompWindowTypeFullscreenMask | + CompWindowTypeDockMask)) + { + if (stackLayerCheck (w, clientLeader, below)) + return below; + } + else + { + return below; + } + break; + default: + /* fullscreen and normal layer */ + if (!(below->type & CompWindowTypeDockMask)) + { + if (stackLayerCheck (w, clientLeader, below)) + return below; + } + break; + } + } + + return NULL; +} + +static void +saveWindowGeometry (CompWindow *w, + int mask) +{ + int m = mask & ~w->saveMask; + + /* only save geometry if window has been placed */ + if (!w->placed) + return; + + if (m & CWX) + w->saveWc.x = w->attrib.x; + + if (m & CWY) + w->saveWc.y = w->attrib.y; + + if (m & CWWidth) + w->saveWc.width = w->attrib.width; + + if (m & CWHeight) + w->saveWc.height = w->attrib.height; + + if (m & CWBorderWidth) + w->saveWc.border_width = w->attrib.border_width; + + w->saveMask |= m; +} + +static int +restoreWindowGeometry (CompWindow *w, + XWindowChanges *xwc, + int mask) +{ + int m = mask & w->saveMask; + + if (m & CWX) + xwc->x = w->saveWc.x; + + if (m & CWY) + xwc->y = w->saveWc.y; + + if (m & CWWidth) + xwc->width = w->saveWc.width; + + if (m & CWHeight) + xwc->height = w->saveWc.height; + + if (m & CWBorderWidth) + xwc->border_width = w->saveWc.border_width; + + w->saveMask &= ~mask; + + return m; +} +static void +configureXWindow (Display *dpy, + CompWindow *w, + unsigned int valueMask, + XWindowChanges *xwc) +{ + XConfigureWindow (w->screen->display->display, w->id, + valueMask, xwc); + + if (w->frame && (valueMask & (CWSibling | CWStackMode))) + XConfigureWindow (w->screen->display->display, w->frame, + valueMask & (CWSibling | CWStackMode), xwc); +} + +static Bool +stackTransients (CompWindow *w, + CompWindow *avoid, + XWindowChanges *xwc) +{ + CompWindow *t; + Window clientLeader = w->clientLeader; + + if (w->transientFor || isGroupTransient (w, clientLeader)) + clientLeader = None; + + for (t = w->screen->reverseWindows; t; t = t->prev) + { + if (t == w || t == avoid) + continue; + + if (t->transientFor == w->id || isGroupTransient (t, clientLeader)) + { + if (!stackTransients (t, avoid, xwc)) + return FALSE; + + if (xwc->sibling == t->id) + return FALSE; + + if (t->mapNum) + configureXWindow (w->screen->display->display, t, + CWSibling | CWStackMode, xwc); + } + } + + return TRUE; +} + +static void +stackAncestors (CompWindow *w, + XWindowChanges *xwc) +{ + if (w->transientFor && xwc->sibling != w->transientFor) + { + CompWindow *ancestor; + + ancestor = findWindowAtScreen (w->screen, w->transientFor); + if (ancestor) + { + if (!stackTransients (ancestor, w, xwc)) + return; + + if (ancestor->mapNum) + configureXWindow (w->screen->display->display, ancestor, + CWSibling | CWStackMode, + xwc); + + stackAncestors (ancestor, xwc); + } + } + else if (isGroupTransient (w, w->clientLeader)) + { + CompWindow *a; + + for (a = w->screen->reverseWindows; a; a = a->prev) + { + if (a->clientLeader == w->clientLeader && + a->transientFor == None && + !isGroupTransient (a, w->clientLeader)) + { + if (xwc->sibling == a->id) + break; + + if (!stackTransients (a, w, xwc)) + break; + + if (a->mapNum) + configureXWindow (w->screen->display->display, a, + CWSibling | CWStackMode, + xwc); + } + } + } +} + +void +updateWindowAttributes (CompWindow *w) +{ + CompWindow *sibiling; + XWindowChanges xwc; + int mask = 0; + + if (w->state & CompWindowStateHiddenMask) + return; + + xwc.stack_mode = Above; + xwc.sibling = None; + + sibiling = findSibilingBelow (w); + if (sibiling) + xwc.sibling = sibiling->id; + + if (xwc.sibling != w->id) + { + if (w->prev) + { + if (xwc.sibling == None) + { + XLowerWindow (w->screen->display->display, w->id); + if (w->frame) + XLowerWindow (w->screen->display->display, w->frame); + } + else if (xwc.sibling != w->prev->id) + mask |= CWSibling | CWStackMode; + } + else if (xwc.sibling != None) + mask |= CWSibling | CWStackMode; + } + + /* only update fullscreen and maximized size if window is visible on + current viewport. Size is updated once we switch to the windows + viewport. */ + if (w->attrib.x < w->screen->width && w->attrib.x + w->width > 0) + { + if (w->type & CompWindowTypeFullscreenMask) + { + saveWindowGeometry (w, + CWX | CWY | CWWidth | CWHeight | + CWBorderWidth); + + xwc.width = w->screen->width; + xwc.height = w->screen->height; + xwc.border_width = 0; + + mask |= CWWidth | CWHeight | CWBorderWidth; + } + else + { + mask |= restoreWindowGeometry (w, &xwc, CWBorderWidth); + + if (w->state & CompWindowStateMaximizedVertMask) + { + saveWindowGeometry (w, CWY | CWHeight); + + xwc.height = w->screen->workArea.height - w->input.top - + w->input.bottom - w->attrib.border_width * 2; + + mask |= CWHeight; + } + else + { + mask |= restoreWindowGeometry (w, &xwc, CWY | CWHeight); + } + + if (w->state & CompWindowStateMaximizedHorzMask) + { + saveWindowGeometry (w, CWX | CWWidth); + + xwc.width = w->screen->workArea.width - w->input.left - + w->input.right - w->attrib.border_width * 2; + + mask |= CWWidth; + } + else + { + mask |= restoreWindowGeometry (w, &xwc, CWX | CWWidth); + } + } + } + + if (mask & (CWWidth | CWHeight)) + { + if (w->type & CompWindowTypeFullscreenMask) + { + xwc.x = 0; + xwc.y = 0; + + mask |= CWX | CWY; + } + else + { + int width, height, max; + + width = (mask & CWWidth) ? xwc.width : w->attrib.width; + height = (mask & CWHeight) ? xwc.height : w->attrib.height; + + xwc.width = w->attrib.width; + xwc.height = w->attrib.height; + + if (constrainNewWindowSize (w, width, height, &width, &height)) + { + xwc.width = width; + xwc.height = height; + } + else + mask &= ~(CWWidth | CWHeight); + + if (w->state & CompWindowStateMaximizedVertMask) + { + if (w->attrib.y < w->screen->workArea.y + w->input.top) + { + xwc.y = w->screen->workArea.y + w->input.top; + mask |= CWY; + } + else + { + height = xwc.height + w->attrib.border_width * 2; + + max = w->screen->workArea.y + w->screen->workArea.height; + if (w->attrib.y + height + w->input.bottom > max) + { + xwc.y = max - height - w->input.bottom; + mask |= CWY; + } + } + } + + if (w->state & CompWindowStateMaximizedHorzMask) + { + if (w->attrib.x < w->screen->workArea.x + w->input.left) + { + xwc.x = w->screen->workArea.x + w->input.left; + mask |= CWX; + } + else + { + width = xwc.width + w->attrib.border_width * 2; + + max = w->screen->workArea.x + w->screen->workArea.width; + if (w->attrib.x + width + w->input.right > max) + { + xwc.x = max - width - w->input.right; + mask |= CWX; + } + } + } + } + } + + if (!mask) + return; + + if (mask & (CWSibling | CWStackMode)) + { + /* a normal window can be stacked above fullscreen windows but we + don't wont normal windows to be stacked above dock window so if + the sibiling we're stacking above is a fullscreen window we also + update all dock windows. */ + if ((sibiling->type & CompWindowTypeFullscreenMask) && + (!(w->type & (CompWindowTypeFullscreenMask | + CompWindowTypeDockMask)))) + { + CompWindow *dw; + + for (dw = w->screen->reverseWindows; dw; dw = dw->prev) + if (dw == sibiling) + break; + + for (; dw; dw = dw->prev) + if (dw->type & CompWindowTypeDockMask) + configureXWindow (w->screen->display->display, dw, + CWSibling | CWStackMode, + &xwc); + } + + /* transient children above */ + if (stackTransients (w, NULL, &xwc)) + { + configureXWindow (w->screen->display->display, w, mask, &xwc); + + /* ancestors, sibilings and sibiling transients below */ + stackAncestors (w, &xwc); + } + } + else + { + configureXWindow (w->screen->display->display, w, mask, &xwc); + } +} + +void +activateWindow (CompWindow *w) +{ + if (w->state & CompWindowStateHiddenMask) + { + if (w->minimized) + unminimizeWindow (w); + + if (w->screen->showingDesktopMask) + leaveShowDesktopMode (w->screen); + + showWindow (w); + } + + if (w->state & CompWindowStateHiddenMask) + return; + + updateWindowAttributes (w); + + if (!(w->type & CompWindowTypeDockMask)) + moveInputFocusToWindow (w); +} + +void +closeWindow (CompWindow *w) +{ + CompDisplay *display = w->screen->display; + + if (w->actions & CompWindowActionCloseMask) + { + XEvent ev; + + ev.type = ClientMessage; + ev.xclient.window = w->id; + ev.xclient.message_type = display->wmProtocolsAtom; + ev.xclient.format = 32; + ev.xclient.data.l[0] = display->wmDeleteWindowAtom; + ev.xclient.data.l[1] = CurrentTime; + ev.xclient.data.l[2] = 0; + ev.xclient.data.l[3] = 0; + ev.xclient.data.l[4] = 0; + + XSendEvent (display->display, w->id, FALSE, NoEventMask, &ev); + } + else + { + XKillClient (display->display, w->id); + } +} + +void +getOuterRectOfWindow (CompWindow *w, + XRectangle *r) +{ + r->x = w->attrib.x - w->input.left; + r->y = w->attrib.y - w->input.top; + r->width = w->width + w->input.left + w->input.right; + r->height = w->height + w->input.top + w->input.bottom; +} + +Bool +constrainNewWindowSize (CompWindow *w, + int width, + int height, + int *newWidth, + int *newHeight) +{ + const XSizeHints *hints = &w->sizeHints; + int min_width = 0; + int min_height = 0; + int base_width = 0; + int base_height = 0; + int xinc = 1; + int yinc = 1; + int max_width = MAXSHORT; + int max_height = MAXSHORT; + + /* Ater gdk_window_constrain_size(), which is partially borrowed from fvwm. + * + * Copyright 1993, Robert Nation + * You may use this code for any purpose, as long as the original + * copyright remains in the source code and all documentation + * + * which in turn borrows parts of the algorithm from uwm + */ + +#define FLOOR(value, base) (((int) ((value) / (base))) * (base)) +#define FLOOR64(value, base) (((uint64_t) ((value) / (base))) * (base)) +#define CLAMP(v, min, max) ((v) <= (min) ? (min) : (v) >= (max) ? (max) : (v)) + + if ((hints->flags & PBaseSize) && (hints->flags & PMinSize)) + { + base_width = hints->base_width; + base_height = hints->base_height; + min_width = hints->min_width; + min_height = hints->min_height; + } + else if (hints->flags & PBaseSize) + { + base_width = hints->base_width; + base_height = hints->base_height; + min_width = hints->base_width; + min_height = hints->base_height; + } + else if (hints->flags & PMinSize) + { + base_width = hints->min_width; + base_height = hints->min_height; + min_width = hints->min_width; + min_height = hints->min_height; + } + + if (hints->flags & PMaxSize) + { + max_width = hints->max_width ; + max_height = hints->max_height; + } + + if (hints->flags & PResizeInc) + { + xinc = MAX (xinc, hints->width_inc); + yinc = MAX (yinc, hints->height_inc); + } + + /* clamp width and height to min and max values */ + width = CLAMP (width, min_width, max_width); + height = CLAMP (height, min_height, max_height); + + /* shrink to base + N * inc */ + width = base_width + FLOOR (width - base_width, xinc); + height = base_height + FLOOR (height - base_height, yinc); + + /* constrain aspect ratio, according to: + * + * min_aspect.x width max_aspect.x + * ------------ <= -------- <= ----------- + * min_aspect.y height max_aspect.y + */ + if (hints->flags & PAspect && + hints->min_aspect.y > 0 && hints->max_aspect.x > 0) + { + /* Use 64 bit arithmetic to prevent overflow */ + + uint64_t min_aspect_x = hints->min_aspect.x; + uint64_t min_aspect_y = hints->min_aspect.y; + uint64_t max_aspect_x = hints->max_aspect.x; + uint64_t max_aspect_y = hints->max_aspect.y; + uint64_t delta; + + if (min_aspect_x * height > width * min_aspect_y) + { + delta = FLOOR64 (height - width * min_aspect_y / min_aspect_x, yinc); + if (height - delta >= min_height) + height -= delta; + else + { + delta = FLOOR64 (height * min_aspect_x / min_aspect_y - width, + xinc); + if (width + delta <= max_width) + width += delta; + } + } + + if (width * max_aspect_y > max_aspect_x * height) + { + delta = FLOOR64 (width - height * max_aspect_x / max_aspect_y, xinc); + if (width - delta >= min_width) + width -= delta; + else + { + delta = FLOOR64 (width * min_aspect_y / max_aspect_y - height, + yinc); + if (height + delta <= max_height) + height += delta; + } + } + } + +#undef FLOOR +#undef FLOOR64 + + if (width != w->attrib.width || height != w->attrib.height) + { + *newWidth = width; + *newHeight = height; + + return TRUE; + } + + return FALSE; +} + +void +hideWindow (CompWindow *w) +{ + if (w->attrib.map_state != IsViewable) + return; + + if (w->state & CompWindowStateHiddenMask) + return; + + if (!w->minimized && !(w->type & w->screen->showingDesktopMask)) + return; + + w->state |= CompWindowStateHiddenMask; + + XUnmapWindow (w->screen->display->display, w->id); + + setWindowState (w->screen->display, w->state, w->id); +} + +void +showWindow (CompWindow *w) +{ + if (!(w->state & CompWindowStateHiddenMask)) + return; + + if (w->minimized || (w->type & w->screen->showingDesktopMask)) + return; + + w->state &= ~CompWindowStateHiddenMask; + + XMapWindow (w->screen->display->display, w->id); + + setWindowState (w->screen->display, w->state, w->id); +} + +static void +minimizeTransients (CompWindow *w, + void *closure) +{ + CompWindow *ancestor = closure; + + if (w->transientFor == ancestor->id || + isGroupTransient (w, ancestor->clientLeader)) + minimizeWindow (w); +} + +void +minimizeWindow (CompWindow *w) +{ + if (!w->minimized) + { + w->minimized = TRUE; + + forEachWindowOnScreen (w->screen, minimizeTransients, (void *) w); + + hideWindow (w); + } +} + +static void +unminimizeTransients (CompWindow *w, + void *closure) +{ + CompWindow *ancestor = closure; + + if (w->transientFor == ancestor->id || + isGroupTransient (w, ancestor->clientLeader)) + unminimizeWindow (w); +} + +void +unminimizeWindow (CompWindow *w) +{ + if (w->minimized) + { + w->minimized = FALSE; + + showWindow (w); + + forEachWindowOnScreen (w->screen, unminimizeTransients, (void *) w); + } +} |