diff options
author | Zack Rusin <zack@kde.org> | 2005-12-31 06:21:45 +0000 |
---|---|---|
committer | Zack Rusin <zack@kde.org> | 2005-12-31 06:21:45 +0000 |
commit | e4b17d9630385493c8bdf29b67a831de9374c939 (patch) | |
tree | 33649c485493ee9b6ff22b7b0cafd21d61fd954c /src |
adding glxcompmgr. currently depends on some mesa patches (will add them in
a second)
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 22 | ||||
-rw-r--r-- | src/display.c | 1149 | ||||
-rw-r--r-- | src/event.c | 268 | ||||
-rw-r--r-- | src/glxcompmgr.c | 146 | ||||
-rw-r--r-- | src/option.c | 256 | ||||
-rw-r--r-- | src/paint.c | 555 | ||||
-rw-r--r-- | src/plugin.c | 358 | ||||
-rw-r--r-- | src/privates.c | 68 | ||||
-rw-r--r-- | src/readpng.c | 199 | ||||
-rw-r--r-- | src/screen.c | 1294 | ||||
-rw-r--r-- | src/texture.c | 286 | ||||
-rw-r--r-- | src/window.c | 605 |
12 files changed, 5206 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..2705fb3 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,22 @@ +INCLUDES = \ + @GLXCOMP_CFLAGS@ \ + -I$(top_srcdir)/include \ + -DPLUGINDIR=\"$(plugindir)\" \ + -DIMAGEDIR=\"$(imagedir)\" + +bin_PROGRAMS = glxcompmgr + +glxcompmgr_LDADD = @GLXCOMP_LIBS@ @GL_LIBS@ -lm +glxcompmgr_LDFLAGS = -export-dynamic +glxcompmgr_SOURCES = \ + glxcompmgr.c \ + privates.c \ + texture.c \ + display.c \ + screen.c \ + window.c \ + event.c \ + paint.c \ + option.c \ + plugin.c \ + readpng.c diff --git a/src/display.c b/src/display.c new file mode 100644 index 0000000..69d01b0 --- /dev/null +++ b/src/display.c @@ -0,0 +1,1149 @@ +/* + * Copyright © 2005 Novell, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without + * fee, provided that the above copyright notice appear in all copies + * and that both that copyright notice and this permission notice + * appear in supporting documentation, and that the name of + * Novell, Inc. not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior permission. + * Novell, Inc. makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + * + * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN + * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Author: David Reveman <davidr@novell.com> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/poll.h> +#include <unistd.h> + +#define XK_MISCELLANY +#include <X11/keysymdef.h> + +#include <X11/Xlib.h> +#include <X11/extensions/Xcomposite.h> +#include <X11/extensions/shape.h> + +#include <comp.h> + +static unsigned int virtualModMask[] = { + CompAltMask, CompMetaMask, CompSuperMask, CompHyperMask, + CompModeSwitchMask, CompNumLockMask, CompScrollLockMask +}; + +typedef struct _CompTimeout { + struct _CompTimeout *next; + int time; + int left; + CallBackProc callBack; + void *closure; + CompTimeoutHandle handle; +} CompTimeout; + +static CompTimeout *timeouts = 0; +static struct timeval lastTimeout; +static CompTimeoutHandle lastTimeoutHandle = 1; + +#define NUM_OPTIONS(d) (sizeof ((d)->opt) / sizeof (CompOption)) + +static char *textureFilter[] = { "Fast", "Good", "Best" }; + +#define NUM_TEXTURE_FILTER (sizeof (textureFilter) / sizeof (textureFilter[0])) + +CompDisplay *compDisplays = 0; + +static CompDisplay compDisplay; + +static char *displayPrivateIndices = 0; +static int displayPrivateLen = 0; + +static int +reallocDisplayPrivate (int size, + void *closure) +{ + CompDisplay *d = compDisplays; + void *privates; + + if (d) + { + privates = realloc (d->privates, size * sizeof (CompPrivate)); + if (!privates) + return FALSE; + + d->privates = (CompPrivate *) privates; + } + + return TRUE; +} + +int +allocateDisplayPrivateIndex (void) +{ + return allocatePrivateIndex (&displayPrivateLen, + &displayPrivateIndices, + reallocDisplayPrivate, + 0); +} + +void +freeDisplayPrivateIndex (int index) +{ + freePrivateIndex (displayPrivateLen, displayPrivateIndices, index); +} + +static void +compDisplayInitOptions (CompDisplay *display, + char **plugin, + int nPlugin) +{ + CompOption *o; + int i; + + o = &display->opt[COMP_DISPLAY_OPTION_ACTIVE_PLUGINS]; + o->name = "active_plugins"; + o->shortDesc = "Active Plugins"; + o->longDesc = "List of currently active plugins"; + o->type = CompOptionTypeList; + o->value.list.type = CompOptionTypeString; + o->value.list.nValue = nPlugin; + o->value.list.value = malloc (sizeof (CompOptionValue) * nPlugin); + for (i = 0; i < nPlugin; i++) + o->value.list.value[i].s = strdup (plugin[i]); + o->rest.s.string = 0; + o->rest.s.nString = 0; + + display->dirtyPluginList = TRUE; + + o = &display->opt[COMP_DISPLAY_OPTION_TEXTURE_FILTER]; + o->name = "texture_filter"; + o->shortDesc = "Texture Filter"; + o->longDesc = "Texture filtering"; + o->type = CompOptionTypeString; + o->value.s = strdup (defaultTextureFilter); + o->rest.s.string = textureFilter; + o->rest.s.nString = NUM_TEXTURE_FILTER; +} + +CompOption * +compGetDisplayOptions (CompDisplay *display, + int *count) +{ + *count = NUM_OPTIONS (display); + return display->opt; +} + +static Bool +setDisplayOption (CompDisplay *display, + char *name, + CompOptionValue *value) +{ + CompOption *o; + int index; + + o = compFindOption (display->opt, NUM_OPTIONS (display), name, &index); + if (!o) + return FALSE; + + switch (index) { + case COMP_DISPLAY_OPTION_ACTIVE_PLUGINS: + if (compSetOptionList (o, value)) + { + display->dirtyPluginList = TRUE; + return TRUE; + } + break; + case COMP_DISPLAY_OPTION_TEXTURE_FILTER: + if (compSetStringOption (o, value)) + { + CompScreen *s; + + for (s = display->screens; s; s = s->next) + damageScreen (s); + + if (strcmp (o->value.s, "Fast") == 0) + display->textureFilter = GL_NEAREST; + else + display->textureFilter = GL_LINEAR; + + return TRUE; + } + default: + break; + } + + return FALSE; +} + +static Bool +setDisplayOptionForPlugin (CompDisplay *display, + char *plugin, + char *name, + CompOptionValue *value) +{ + CompPlugin *p; + + p = findActivePlugin (plugin); + if (p && p->vTable->setDisplayOption) + return (*p->vTable->setDisplayOption) (display, name, value); + + return FALSE; +} + +static Bool +initPluginForDisplay (CompPlugin *p, + CompDisplay *d) +{ + return (*p->vTable->initDisplay) (p, d); +} + +static void +finiPluginForDisplay (CompPlugin *p, + CompDisplay *d) +{ + (*p->vTable->finiDisplay) (p, d); +} + +static void +updatePlugins (CompDisplay *d) +{ + CompOption *o; + CompPlugin *p, **pop = 0; + int nPop, i, j; + + d->dirtyPluginList = FALSE; + + o = &d->opt[COMP_DISPLAY_OPTION_ACTIVE_PLUGINS]; + for (i = 0; i < d->plugin.list.nValue && i < o->value.list.nValue; i++) + { + if (strcmp (d->plugin.list.value[i].s, o->value.list.value[i].s)) + break; + } + + nPop = d->plugin.list.nValue - i; + + if (nPop) + { + pop = malloc (sizeof (CompPlugin *) * nPop); + if (!pop) + { + (*d->setDisplayOption) (d, o->name, &d->plugin); + return; + } + } + + for (j = 0; j < nPop; j++) + { + pop[j] = popPlugin (); + d->plugin.list.nValue--; + free (d->plugin.list.value[d->plugin.list.nValue].s); + } + + for (; i < o->value.list.nValue; i++) + { + p = 0; + for (j = 0; j < nPop; j++) + { + if (pop[j] && strcmp (pop[j]->vTable->name, + o->value.list.value[i].s) == 0) + { + if (pushPlugin (pop[j])) + { + p = pop[j]; + pop[j] = 0; + break; + } + } + } + + if (p == 0) + { + p = loadPlugin (o->value.list.value[i].s); + if (p) + { + if (!pushPlugin (p)) + { + unloadPlugin (p); + p = 0; + } + } + } + + if (p) + { + CompOptionValue *value; + + value = realloc (d->plugin.list.value, sizeof (CompOption) * + (d->plugin.list.nValue + 1)); + if (value) + { + value[d->plugin.list.nValue].s = strdup (p->vTable->name); + + d->plugin.list.value = value; + d->plugin.list.nValue++; + } + else + { + p = popPlugin (); + unloadPlugin (p); + } + } + } + + for (j = 0; j < nPop; j++) + { + if (pop[j]) + unloadPlugin (pop[j]); + } + + if (nPop) + free (pop); + + (*d->setDisplayOption) (d, o->name, &d->plugin); +} + +static void +addTimeout (CompTimeout *timeout) +{ + CompTimeout *p = 0, *t; + + for (t = timeouts; t; t = t->next) + { + if (timeout->time < t->left) + break; + + p = t; + } + + timeout->next = t; + timeout->left = timeout->time; + + if (p) + p->next = timeout; + else + timeouts = timeout; +} + +CompTimeoutHandle +compAddTimeout (int time, + CallBackProc callBack, + void *closure) +{ + CompTimeout *timeout; + + timeout = malloc (sizeof (CompTimeout)); + if (!timeout) + return 0; + + timeout->time = time; + timeout->callBack = callBack; + timeout->closure = closure; + timeout->handle = lastTimeoutHandle++; + + if (lastTimeoutHandle == MAXSHORT) + lastTimeoutHandle = 1; + + if (!timeouts) + gettimeofday (&lastTimeout, 0); + + addTimeout (timeout); + + return timeout->handle; +} + +void +compRemoveTimeout (CompTimeoutHandle handle) +{ + CompTimeout *p = 0, *t; + + for (t = timeouts; t; t = t->next) + { + if (t->handle == handle) + break; + + p = t; + } + + if (t) + { + if (p) + p->next = t->next; + else + timeouts = t->next; + + free (t); + } +} + +#define TIMEVALDIFF(tv1, tv2) \ + ((tv1)->tv_sec == (tv2)->tv_sec || (tv1)->tv_usec >= (tv2)->tv_usec) ? \ + ((((tv1)->tv_sec - (tv2)->tv_sec) * 1000000) + \ + ((tv1)->tv_usec - (tv2)->tv_usec)) / 1000 : \ + ((((tv1)->tv_sec - 1 - (tv2)->tv_sec) * 1000000) + \ + (1000000 + (tv1)->tv_usec - (tv2)->tv_usec)) / 1000 + +static int +getTimeToNextRedraw (CompScreen *s, + struct timeval *lastTv) +{ + struct timeval tv; + int diff; + + gettimeofday (&tv, 0); + + diff = TIMEVALDIFF (&tv, lastTv); + + if (diff > s->redrawTime) + return 0; + + return s->redrawTime - diff; +} + +static CompWindow * +findWindowAt (CompDisplay *d, + Window root, + int x, + int y) +{ + CompScreen *s; + CompWindow *w; + + for (s = d->screens; s; s = s->next) + { + if (s->root == root && s->maxGrab == 0) + { + for (w = s->reverseWindows; w; w = w->prev) + { + if (x >= w->attrib.x && + y >= w->attrib.y && + x < w->attrib.x + w->width && + y < w->attrib.y + w->height) + return w; + } + } + } + + return 0; +} + +static Window +translateToRootWindow (CompDisplay *d, + Window child) +{ + CompScreen *s; + + for (s = d->screens; s; s = s->next) + { + if (s->root == child || s->grabWindow == child) + return s->root; + } + + return child; +} + +void +updateModifierMappings (CompDisplay *d) +{ + XModifierKeymap *modmap; + unsigned int i, modMask[CompModNum]; + + for (i = 0; i < CompModNum; i++) + modMask[i] = 0; + + modmap = XGetModifierMapping (d->display); + if (modmap && modmap->max_keypermod > 0) + { + static int maskTable[] = { + ShiftMask, LockMask, ControlMask, Mod1Mask, + Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask + }; + KeySym keysym; + int i, size, mask; + + size = (sizeof (maskTable) / sizeof (int)) * modmap->max_keypermod; + + for (i = 0; i < size; ++i) + { + if (!modmap->modifiermap[i]) + continue; + + keysym = XKeycodeToKeysym (d->display, modmap->modifiermap[i], 0); + if (keysym) + { + mask = maskTable[i / modmap->max_keypermod]; + + if (keysym == XK_Alt_L || + keysym == XK_Alt_R) + { + modMask[CompModAlt] |= mask; + } + else if (keysym == XK_Meta_L || + keysym == XK_Meta_R) + { + modMask[CompModMeta] |= mask; + } + else if (keysym == XK_Super_L || + keysym == XK_Super_R) + { + modMask[CompModSuper] |= mask; + } + else if (keysym == XK_Hyper_L || + keysym == XK_Hyper_R) + { + modMask[CompModHyper] |= mask; + } + else if (keysym == XK_Mode_switch) + { + modMask[CompModModeSwitch] |= mask; + } + else if (keysym == XK_Scroll_Lock) + { + modMask[CompModScrollLock] |= mask; + } + else if (keysym == XK_Num_Lock) + { + modMask[CompModNumLock] |= mask; + } + } + } + + if (modmap) + XFreeModifiermap (modmap); + + for (i = 0; i < CompModNum; i++) + { + if (!modMask[i]) + modMask[i] = CompNoMask; + } + + if (memcmp (modMask, d->modMask, sizeof (modMask))) + { + CompScreen *s; + + memcpy (d->modMask, modMask, sizeof (modMask)); + + for (s = d->screens; s; s = s->next) + updatePassiveGrabs (s); + } + } +} + +unsigned int +virtualToRealModMask (CompDisplay *d, + unsigned int modMask) +{ + int i; + + for (i = 0; i < CompModNum; i++) + { + if (modMask & virtualModMask[i]) + { + modMask &= ~virtualModMask[i]; + modMask |= d->modMask[i]; + } + } + + return (modMask & ~(CompPressMask | CompReleaseMask)); +} + +static unsigned int +realToVirtualModMask (CompDisplay *d, + unsigned int modMask) +{ + int i; + + for (i = 0; i < CompModNum; i++) + { + if (modMask & d->modMask[i]) + modMask |= virtualModMask[i]; + } + + return modMask; +} + +void +eventLoop (void) +{ + XEvent event; + struct pollfd ufd; + int timeDiff; + struct timeval tv; + Region tmpRegion; + CompDisplay *display = compDisplays; + CompScreen *s = display->screens; + int timeToNextRedraw = 0; + CompWindow *move = 0; + int px = 0, py = 0; + CompTimeout *t; + + tmpRegion = XCreateRegion (); + if (!tmpRegion) + { + fprintf (stderr, "%s: Couldn't create region\n", programName); + return; + } + + ufd.fd = ConnectionNumber (display->display); + ufd.events = POLLIN; + + for (;;) + { + if (display->dirtyPluginList) + updatePlugins (display); + + if (restartSignal) + { + execvp (programName, programArgv); + exit (1); + } + + while (XPending (display->display)) + { + XNextEvent (display->display, &event); + + /* translate root window */ + if (testMode) + { + Window root, child; + + switch (event.type) { + case ButtonPress: + if (!move) + { + px = event.xbutton.x; + py = event.xbutton.y; + + move = findWindowAt (display, event.xbutton.window, + px, py); + if (move) + { + XRaiseWindow (display->display, move->id); + continue; + } + } + case ButtonRelease: + move = 0; + + root = translateToRootWindow (display, + event.xbutton.window); + XTranslateCoordinates (display->display, + event.xbutton.root, root, + event.xbutton.x_root, + event.xbutton.y_root, + &event.xbutton.x_root, + &event.xbutton.y_root, + &child); + event.xbutton.root = root; + break; + case KeyPress: + case KeyRelease: + root = translateToRootWindow (display, event.xkey.window); + XTranslateCoordinates (display->display, + event.xkey.root, root, + event.xkey.x_root, + event.xkey.y_root, + &event.xkey.x_root, + &event.xkey.y_root, + &child); + event.xkey.root = root; + break; + case MotionNotify: + if (move) + { + XMoveWindow (display->display, move->id, + move->attrib.x + event.xbutton.x - px, + move->attrib.y + event.xbutton.y - py); + + px = event.xbutton.x; + py = event.xbutton.y; + continue; + } + + root = translateToRootWindow (display, + event.xmotion.window); + XTranslateCoordinates (display->display, + event.xmotion.root, root, + event.xmotion.x_root, + event.xmotion.y_root, + &event.xmotion.x_root, + &event.xmotion.y_root, + &child); + event.xmotion.root = root; + default: + break; + } + } + + /* add virtual modifiers */ + switch (event.type) { + case ButtonPress: + event.xbutton.state |= CompPressMask; + event.xbutton.state = + realToVirtualModMask (display, event.xbutton.state); + break; + case ButtonRelease: + event.xbutton.state |= CompReleaseMask; + event.xbutton.state = + realToVirtualModMask (display, event.xbutton.state); + break; + case KeyPress: + event.xkey.state |= CompPressMask; + event.xkey.state = realToVirtualModMask (display, + event.xkey.state); + break; + case KeyRelease: + event.xkey.state |= CompReleaseMask; + event.xkey.state = realToVirtualModMask (display, + event.xkey.state); + break; + case MotionNotify: + event.xmotion.state = + realToVirtualModMask (display, event.xmotion.state); + break; + default: + break; + } + + (*display->handleEvent) (display, &event); + } + + if (s->allDamaged || REGION_NOT_EMPTY (s->damage)) + { + if (timeToNextRedraw == 0) + { + /* wait for X drawing requests to finish + glXWaitX (); */ + + gettimeofday (&tv, 0); + + timeDiff = TIMEVALDIFF (&tv, &s->lastRedraw); + + (*s->preparePaintScreen) (s, timeDiff); + + if (s->allDamaged) + { + EMPTY_REGION (s->damage); + s->allDamaged = 0; + + (*s->paintScreen) (s, + &defaultScreenPaintAttrib, + &defaultWindowPaintAttrib, + &s->region, + PAINT_SCREEN_REGION_MASK | + PAINT_SCREEN_FULL_MASK); + + glXSwapBuffers (s->display->display, s->root); + } + else + { + XIntersectRegion (s->damage, &s->region, tmpRegion); + + EMPTY_REGION (s->damage); + + if ((*s->paintScreen) (s, + &defaultScreenPaintAttrib, + &defaultWindowPaintAttrib, + tmpRegion, + PAINT_SCREEN_REGION_MASK)) + { + BoxPtr pBox; + int nBox, y; + + glEnable (GL_SCISSOR_TEST); + glDrawBuffer (GL_FRONT); + + pBox = tmpRegion->rects; + nBox = tmpRegion->numRects; + while (nBox--) + { + y = s->height - pBox->y2; + + glBitmap (0, 0, 0, 0, + pBox->x1 - s->rasterX, y - s->rasterY, + NULL); + + s->rasterX = pBox->x1; + s->rasterY = y; + + glScissor (pBox->x1, y, + pBox->x2 - pBox->x1, + pBox->y2 - pBox->y1); + + glCopyPixels (pBox->x1, y, + pBox->x2 - pBox->x1, + pBox->y2 - pBox->y1, + GL_COLOR); + + pBox++; + } + + glDrawBuffer (GL_BACK); + glDisable (GL_SCISSOR_TEST); + glFlush (); + } + else + { + (*s->paintScreen) (s, + &defaultScreenPaintAttrib, + &defaultWindowPaintAttrib, + &s->region, + PAINT_SCREEN_FULL_MASK); + + glXSwapBuffers (s->display->display, s->root); + } + } + + s->lastRedraw = tv; + + (*s->donePaintScreen) (s); + + /* remove destroyed windows */ + while (s->pendingDestroys) + { + CompWindow *w; + + for (w = s->windows; w; w = w->next) + { + if (w->destroyed) + { + addWindowDamage (w); + removeWindow (w); + break; + } + } + + s->pendingDestroys--; + } + } + + timeToNextRedraw = getTimeToNextRedraw (s, &s->lastRedraw); + if (timeToNextRedraw) + timeToNextRedraw = poll (&ufd, 1, timeToNextRedraw); + } + else + { + if (timeouts) + { + if (timeouts->left > 0) + poll (&ufd, 1, timeouts->left); + + gettimeofday (&tv, 0); + + timeDiff = TIMEVALDIFF (&tv, &lastTimeout); + + for (t = timeouts; t; t = t->next) + t->left -= timeDiff; + + while (timeouts && timeouts->left <= 0) + { + t = timeouts; + if ((*t->callBack) (t->closure)) + { + timeouts = t->next; + addTimeout (t); + } + else + { + timeouts = t->next; + free (t); + } + } + + s->lastRedraw = lastTimeout = tv; + } + else + { + poll (&ufd, 1, 1000); + gettimeofday (&s->lastRedraw, 0); + } + + /* just redraw immediately */ + timeToNextRedraw = 0; + } + } +} + +static int errors = 0; +static int redirectFailed; + +static int +errorHandler (Display *dpy, + XErrorEvent *e) +{ + +#ifdef DEBUG + char str[128]; + char *name = 0; + int o; +#endif + + errors++; + + if (e->request_code == compDisplays->compositeOpcode && + e->minor_code == X_CompositeRedirectSubwindows) + { + redirectFailed = 1; + return 0; + } + +#ifdef DEBUG + XGetErrorDatabaseText (dpy, "XlibMessage", "XError", "", str, 128); + fprintf (stderr, "%s", str); + + o = e->error_code - compDisplays->damageError; + switch (o) { + case BadDamage: + name = "BadDamage"; + break; + default: + break; + } + + if (name) + { + fprintf (stderr, ": %s\n ", name); + } + else + { + XGetErrorText (dpy, e->error_code, str, 128); + fprintf (stderr, ": %s\n ", str); + } + + XGetErrorDatabaseText (dpy, "XlibMessage", "MajorCode", "%d", str, 128); + fprintf (stderr, str, e->request_code); + + sprintf (str, "%d", e->request_code); + XGetErrorDatabaseText (dpy, "XRequest", str, "", str, 128); + if (strcmp (str, "")) + fprintf (stderr, " (%s)", str); + fprintf (stderr, "\n "); + + XGetErrorDatabaseText (dpy, "XlibMessage", "MinorCode", "%d", str, 128); + fprintf (stderr, str, e->minor_code); + fprintf (stderr, "\n "); + + XGetErrorDatabaseText (dpy, "XlibMessage", "ResourceID", "%d", str, 128); + fprintf (stderr, str, e->resourceid); + fprintf (stderr, "\n"); + + /* abort (); */ +#endif + + return 0; +} + +int +compCheckForError (void) +{ + int e; + + e = errors; + errors = 0; + + return e; +} + +Bool +addDisplay (char *name, + char **plugin, + int nPlugin) +{ + CompDisplay *d; + Display *dpy; + int i; + + d = &compDisplay; + + if (displayPrivateLen) + { + d->privates = malloc (displayPrivateLen * sizeof (CompPrivate)); + if (!d->privates) + return FALSE; + } + else + d->privates = 0; + + d->screenPrivateIndices = 0; + d->screenPrivateLen = 0; + + for (i = 0; i < CompModNum; i++) + d->modMask[i] = CompNoMask; + + d->plugin.list.type = CompOptionTypeString; + d->plugin.list.nValue = 0; + d->plugin.list.value = 0; + + compDisplayInitOptions (d, plugin, nPlugin); + + d->textureFilter = GL_LINEAR; + + d->display = dpy = XOpenDisplay (name); + if (!d->display) + { + fprintf (stderr, "%s: Couldn't open display %s\n", + programName, XDisplayName (name)); + return FALSE; + } + +#ifdef DEBUG + XSynchronize (dpy, TRUE); +#endif + + XSetErrorHandler (errorHandler); + + updateModifierMappings (d); + + d->setDisplayOption = setDisplayOption; + d->setDisplayOptionForPlugin = setDisplayOptionForPlugin; + + d->initPluginForDisplay = initPluginForDisplay; + d->finiPluginForDisplay = finiPluginForDisplay; + + d->handleEvent = handleEvent; + d->handleDamageEvent = handleDamageEvent; + + d->winTypeAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE", 0); + d->winDesktopAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_DESKTOP", 0); + d->winDockAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_DOCK", 0); + d->winToolbarAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_TOOLBAR", 0); + d->winMenuAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_MENU", 0); + d->winUtilAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_UTILITY", 0); + d->winSplashAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_SPLASH", 0); + d->winDialogAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_DIALOG", 0); + d->winNormalAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_NORMAL", 0); + d->winOpacityAtom = XInternAtom (dpy, "_NET_WM_WINDOW_OPACITY", 0); + d->winActiveAtom = XInternAtom (dpy, "_NET_ACTIVE_WINDOW", 0); + + d->wmStateAtom = XInternAtom (dpy, "WM_STATE", 0); + d->wmDeleteWindowAtom = XInternAtom (dpy, "WM_DELETE_WINDOW", 0); + + d->xBackgroundAtom[0] = XInternAtom (dpy, "_XSETROOT_ID", 0); + d->xBackgroundAtom[1] = XInternAtom (dpy, "_XROOTPMAP_ID", 0); + + if (testMode) + { + d->compositeOpcode = MAXSHORT; + d->compositeEvent = MAXSHORT; + d->compositeError = MAXSHORT; + + d->damageEvent = MAXSHORT; + d->damageError = MAXSHORT; + } + else + { + int compositeMajor, compositeMinor; + + if (!XQueryExtension (dpy, + COMPOSITE_NAME, + &d->compositeOpcode, + &d->compositeEvent, + &d->compositeError)) + { + fprintf (stderr, "%s: No composite extension\n", programName); + return FALSE; + } + + XCompositeQueryVersion (dpy, &compositeMajor, &compositeMinor); + if (compositeMajor == 0 && compositeMinor < 2) + { + fprintf (stderr, "%s: Old composite extension\n", programName); + return FALSE; + } + + if (!XDamageQueryExtension (dpy, &d->damageEvent, &d->damageError)) + { + fprintf (stderr, "%s: No damage extension\n", programName); + return FALSE; + } + } + + d->shapeExtension = XShapeQueryExtension (dpy, + &d->shapeEvent, + &d->shapeError); + + compDisplays = d; + + if (testMode) + { + addScreen (d, 0); + } + else + { + XGrabServer (dpy); + + for (i = 0; i < ScreenCount (dpy); i++) + { + redirectFailed = 0; + XCompositeRedirectSubwindows (dpy, XRootWindow (dpy, i), + CompositeRedirectManual); + XSync (dpy, FALSE); + if (redirectFailed) + { + fprintf (stderr, "%s: Another composite manager is already " + "running on screen: %d\n", programName, i); + } + else + { + if (!addScreen (d, i)) + { + fprintf (stderr, "%s: Failed to manage screen: %d\n", + programName, i); + } + } + } + + XUngrabServer (dpy); + } + + if (!d->screens) + { + fprintf (stderr, "%s: No managable screens found on display %s\n", + programName, XDisplayName (name)); + return FALSE; + } + + return TRUE; +} + +CompScreen * +findScreenAtDisplay (CompDisplay *d, + Window root) +{ + CompScreen *s; + + for (s = d->screens; s; s = s->next) + { + if (s->root == root) + return s; + } + + return 0; +} + +CompWindow * +findWindowAtDisplay (CompDisplay *d, + Window id) +{ + CompScreen *s; + CompWindow *w; + + for (s = d->screens; s; s = s->next) + { + w = findWindowAtScreen (s, id); + if (w) + return w; + } + + return 0; +} diff --git a/src/event.c b/src/event.c new file mode 100644 index 0000000..ccabfca --- /dev/null +++ b/src/event.c @@ -0,0 +1,268 @@ +/* + * Copyright © 2005 Novell, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without + * fee, provided that the above copyright notice appear in all copies + * and that both that copyright notice and this permission notice + * appear in supporting documentation, and that the name of + * Novell, Inc. not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior permission. + * Novell, Inc. makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + * + * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN + * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Author: David Reveman <davidr@novell.com> + */ + +#include <stdlib.h> + +#include <X11/Xlib.h> +#include <X11/extensions/shape.h> + +#include <comp.h> + +void +handleEvent (CompDisplay *display, + XEvent *event) +{ + CompScreen *s; + CompWindow *w; + + switch (event->type) { + case Expose: + s = findScreenAtDisplay (display, event->xexpose.window); + if (s) + { + int more = event->xexpose.count + 1; + + if (s->nExpose == s->sizeExpose) + { + if (s->exposeRects) + { + s->exposeRects = realloc (s->exposeRects, + (s->sizeExpose + more) * + sizeof (XRectangle)); + s->sizeExpose += more; + } + else + { + s->exposeRects = malloc (more * sizeof (XRectangle)); + s->sizeExpose = more; + } + } + + s->exposeRects[s->nExpose].x = event->xexpose.x; + s->exposeRects[s->nExpose].y = event->xexpose.y; + s->exposeRects[s->nExpose].width = event->xexpose.width; + s->exposeRects[s->nExpose].height = event->xexpose.height; + s->nExpose++; + + if (event->xexpose.count == 0) + { + REGION rect; + + rect.rects = &rect.extents; + rect.numRects = rect.size = 1; + + while (s->nExpose--) + { + rect.extents.x1 = s->exposeRects[s->nExpose].x; + rect.extents.y1 = s->exposeRects[s->nExpose].y; + rect.extents.x2 = rect.extents.x1 + + s->exposeRects[s->nExpose].width; + rect.extents.y2 = rect.extents.y1 + + s->exposeRects[s->nExpose].height; + + damageScreenRegion (s, &rect); + } + s->nExpose = 0; + } + } + break; + case ConfigureNotify: + w = findWindowAtDisplay (display, event->xconfigure.window); + if (w) + { + configureWindow (w, &event->xconfigure); + } + else + { + s = findScreenAtDisplay (display, event->xconfigure.window); + if (s) + configureScreen (s, &event->xconfigure); + } + break; + case CreateNotify: + s = findScreenAtDisplay (display, event->xcreatewindow.parent); + if (s) + addWindow (s, event->xcreatewindow.window, 0); + break; + case DestroyNotify: + w = findWindowAtDisplay (display, event->xdestroywindow.window); + if (w) + { + addWindowDamage (w); + removeWindow (w); + } + break; + case MapNotify: + w = findWindowAtDisplay (display, event->xmap.window); + if (w) + mapWindow (w); + break; + case UnmapNotify: + w = findWindowAtDisplay (display, event->xunmap.window); + if (w) + unmapWindow (w); + break; + case ReparentNotify: + s = findScreenAtDisplay (display, event->xreparent.parent); + if (s) + { + addWindow (s, event->xreparent.window, 0); + } + else + { + w = findWindowAtDisplay (display, event->xreparent.window); + if (w) + { + addWindowDamage (w); + removeWindow (w); + } + } + break; + case CirculateNotify: + w = findWindowAtDisplay (display, event->xcirculate.window); + if (w) + circulateWindow (w, &event->xcirculate); + break; + case ButtonPress: + case ButtonRelease: + case KeyPress: + case KeyRelease: + break; + case PropertyNotify: + if (event->xproperty.atom == display->winActiveAtom) + { + s = findScreenAtDisplay (display, event->xproperty.window); + if (s) + s->activeWindow = getActiveWindow (display, s->root); + } + else if (event->xproperty.atom == display->winTypeAtom) + { + w = findWindowAtDisplay (display, event->xproperty.window); + if (w) + { + Atom type; + + type = getWindowType (display, w->client); + if (type != w->type) + { + + if (w->attrib.map_state == IsViewable) + { + if (w->type == display->winDesktopAtom) + w->screen->desktopWindowCount--; + else if (type == display->winDesktopAtom) + w->screen->desktopWindowCount++; + + addWindowDamage (w); + } + w->type = type; + } + } + } + else if (event->xproperty.atom == display->winOpacityAtom) + { + w = findWindowAtDisplay (display, event->xproperty.window); + if (w) + { + GLuint opacity; + + opacity = getWindowOpacity (display, w->id); + if (opacity != w->opacity) + { + w->opacity = opacity; + if (w->attrib.map_state == IsViewable) + addWindowDamage (w); + } + } + } + else if (event->xproperty.atom == display->xBackgroundAtom[0] || + event->xproperty.atom == display->xBackgroundAtom[1]) + { + s = findScreenAtDisplay (display, event->xproperty.window); + if (s) + { + finiTexture (s, &s->backgroundTexture); + initTexture (s, &s->backgroundTexture); + + damageScreen (s); + } + } + break; + case MotionNotify: + break; + case ClientMessage: + if (event->xclient.format == 32 && + event->xclient.data.l[0] == display->wmDeleteWindowAtom) + exit (0); + break; + case MappingNotify: + updateModifierMappings (display); + break; + default: + if (display->shapeExtension && + event->type == display->shapeEvent + ShapeNotify) + { + w = findWindowAtDisplay (display, ((XShapeEvent *) event)->window); + if (w) + updateWindowRegion (w); + } + else if (event->type == display->damageEvent + XDamageNotify) + { + (*display->handleDamageEvent) (display, + (XDamageNotifyEvent *) event); + } + break; + } +} + +void +handleDamageEvent (CompDisplay *display, + XDamageNotifyEvent *event) +{ + CompScreen *screen; + REGION rect; + + rect.rects = &rect.extents; + rect.numRects = rect.size = 1; + + screen = display->screens; + if (screen->next) + { + CompWindow *w; + + w = findWindowAtDisplay (display, event->drawable); + if (!w) + return; + + screen = w->screen; + } + + rect.extents.x1 = event->geometry.x + event->area.x; + rect.extents.y1 = event->geometry.y + event->area.y; + rect.extents.x2 = rect.extents.x1 + event->area.width; + rect.extents.y2 = rect.extents.y1 + event->area.height; + + damageScreenRegion (screen, &rect); +} diff --git a/src/glxcompmgr.c b/src/glxcompmgr.c new file mode 100644 index 0000000..21673f3 --- /dev/null +++ b/src/glxcompmgr.c @@ -0,0 +1,146 @@ +/* + * Copyright © 2005 Novell, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without + * fee, provided that the above copyright notice appear in all copies + * and that both that copyright notice and this permission notice + * appear in supporting documentation, and that the name of + * Novell, Inc. not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior permission. + * Novell, Inc. makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + * + * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN + * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Author: David Reveman <davidr@novell.com> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <signal.h> +#include <unistd.h> +#include <string.h> + +#include <comp.h> + +char *programName; +char **programArgv; +int programArgc; + +char *backgroundImage = "background.png"; +char *windowImage = "window.png"; + +REGION emptyRegion; +GLushort defaultColor[4] = { 0, 0, 0, 0 }; +Window currentRoot = 0; + +int defaultRefreshRate = 60; +char *defaultTextureFilter = "Good"; + +Bool testMode = FALSE; +Bool restartSignal = FALSE; + +static void +usage (void) +{ + printf ("Usage: %s " + "[--display DISPLAY] " + "[--bg-image PNG] " + "[--window-image PNG]\n " + "[--refresh-rate RATE] " + "[--fast-filter] " + "[--test-mode]\n " + "[--help] " + "[PLUGIN]...\n", + programName); +} + +static void +signalHandler (int sig) +{ + if (sig == SIGHUP) + restartSignal = TRUE; +} + +int +main (int argc, char **argv) +{ + char *displayName = 0; + char *plugin[256]; + int i, nPlugin = 0; + + programName = argv[0]; + programArgc = argc; + programArgv = argv; + + signal (SIGHUP, signalHandler); + + emptyRegion.rects = &emptyRegion.extents; + emptyRegion.numRects = 0; + emptyRegion.extents.x1 = 0; + emptyRegion.extents.y1 = 0; + emptyRegion.extents.x2 = 0; + emptyRegion.extents.y2 = 0; + emptyRegion.size = 0; + + for (i = 1; i < argc; i++) + { + if (!strcmp (argv[i], "--help")) + { + usage (); + return 0; + } + else if (!strcmp (argv[i], "--display")) + { + if (i + 1 < argc) + displayName = argv[++i]; + } + else if (!strcmp (argv[i], "--refresh-rate")) + { + if (i + 1 < argc) + { + defaultRefreshRate = atoi (programArgv[++i]); + defaultRefreshRate = RESTRICT_VALUE (defaultRefreshRate, + 1, 1000); + } + } + else if (!strcmp (argv[i], "--fast-filter")) + { + defaultTextureFilter = "Fast"; + } + else if (!strcmp (argv[i], "--test-mode")) + { + testMode = TRUE; + } + else if (!strcmp (argv[i], "--bg-image")) + { + if (i + 1 < argc) + backgroundImage = argv[++i]; + } + else if (!strcmp (argv[i], "--window-image")) + { + if (i + 1 < argc) + windowImage = argv[++i]; + } + else + { + if (nPlugin < 256) + plugin[nPlugin++] = argv[i]; + } + } + + if (!addDisplay (displayName, plugin, nPlugin)) + return 1; + + eventLoop (); + + return 0; +} diff --git a/src/option.c b/src/option.c new file mode 100644 index 0000000..7369b5e --- /dev/null +++ b/src/option.c @@ -0,0 +1,256 @@ +/* + * Copyright © 2005 Novell, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without + * fee, provided that the above copyright notice appear in all copies + * and that both that copyright notice and this permission notice + * appear in supporting documentation, and that the name of + * Novell, Inc. not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior permission. + * Novell, Inc. makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + * + * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN + * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Author: David Reveman <davidr@novell.com> + */ + +#include <stdlib.h> +#include <string.h> +#include <math.h> + +#include <comp.h> + +CompOption * +compFindOption (CompOption *option, + int nOption, + char *name, + int *index) +{ + int i; + + for (i = 0; i < nOption; i++) + { + if (strcmp (option[i].name, name) == 0) + { + if (index) + *index = i; + + return &option[i]; + } + } + + return 0; +} + +Bool +compSetBoolOption (CompOption *option, + CompOptionValue *value) +{ + option->value.i = (value->b) ? TRUE : FALSE; + + return TRUE; +} + +Bool +compSetIntOption (CompOption *option, + CompOptionValue *value) +{ + if (value->i <= option->rest.i.min || + value->i >= option->rest.i.max || + value->i == option->value.i) + return FALSE; + + option->value.i = value->i; + + return TRUE; +} + +Bool +compSetFloatOption (CompOption *option, + CompOptionValue *value) +{ + float v, p; + + p = 1.0f / option->rest.f.precision; + v = ((int) (value->f * p + 0.5f)) / p; + + if (v <= option->rest.f.min || + v >= option->rest.f.max || + v == option->value.f) + return FALSE; + + option->value.f = v; + + return TRUE; +} + +Bool +compSetStringOption (CompOption *option, + CompOptionValue *value) +{ + if (option->rest.s.nString) + { + int i; + + if (!value->s) + return FALSE; + + for (i = 0; i < option->rest.s.nString; i++) + { + if (strcmp (option->rest.s.string[i], value->s) == 0) + break; + } + + if (i == option->rest.s.nString) + return FALSE; + } + + if (option->value.s == value->s) + return FALSE; + + if (option->value.s && value->s) + { + if (strcmp (option->value.s, value->s) == 0) + return FALSE; + } + + if (option->value.s) + free (option->value.s); + + if (value->s) + option->value.s = strdup (value->s); + else + option->value.s = 0; + + return TRUE; +} + +Bool +compSetColorOption (CompOption *option, + CompOptionValue *value) +{ + if (memcmp (value->c, option->value.c, sizeof (value->c)) == 0) + return FALSE; + + memcpy (option->value.c, value->c, sizeof (value->c)); + + return TRUE; +} + +Bool +compSetBindingOption (CompOption *option, + CompOptionValue *value) +{ + CompBinding *binding; + + binding = &option->value.bind; + if (value->bind.type == CompBindingTypeButton) + { + if (binding->type == CompBindingTypeButton && + binding->u.button.button == value->bind.u.button.button && + binding->u.button.modifiers == value->bind.u.button.modifiers) + return FALSE; + } + else + { + if (binding->type == CompBindingTypeKey && + binding->u.key.keycode == value->bind.u.key.keycode && + binding->u.key.modifiers == value->bind.u.key.modifiers) + return FALSE; + } + + *binding = value->bind; + + return TRUE; +} + +Bool +compSetOptionList (CompOption *option, + CompOptionValue *value) +{ + CompOption o; + Bool status = FALSE; + int i, min; + + if (value->list.nValue != option->value.list.nValue) + { + CompOptionValue *v; + + v = malloc (sizeof (CompOptionValue) * value->list.nValue); + if (!v) + return FALSE; + + min = MIN (value->list.nValue, option->value.list.nValue); + + if (min < option->value.list.nValue) + { + switch (option->value.list.type) { + case CompOptionTypeString: + for (i = min; i < option->value.list.nValue; i++) + { + if (option->value.list.value[i].s) + free (option->value.list.value[i].s); + } + default: + break; + } + } + + memset (v, 0, sizeof (CompOptionValue) * value->list.nValue); + + if (min) + memcpy (v, option->value.list.value, + sizeof (CompOptionValue) * min); + + if (option->value.list.value) + free (option->value.list.value); + + option->value.list.value = v; + option->value.list.nValue = value->list.nValue; + + status = TRUE; + } + + o = *option; + o.type = option->value.list.type; + + for (i = 0; i < value->list.nValue; i++) + { + o.value = option->value.list.value[i]; + + switch (o.type) { + case CompOptionTypeBool: + status |= compSetBoolOption (&o, &value->list.value[i]); + break; + case CompOptionTypeInt: + status |= compSetIntOption (&o, &value->list.value[i]); + break; + case CompOptionTypeFloat: + status |= compSetFloatOption (&o, &value->list.value[i]); + break; + case CompOptionTypeString: + status |= compSetStringOption (&o, &value->list.value[i]); + break; + case CompOptionTypeColor: + status |= compSetColorOption (&o, &value->list.value[i]); + break; + case CompOptionTypeBinding: + status |= compSetBindingOption (&o, &value->list.value[i]); + default: + break; + } + + option->value.list.value[i] = o.value; + } + + return status; +} diff --git a/src/paint.c b/src/paint.c new file mode 100644 index 0000000..5174e95 --- /dev/null +++ b/src/paint.c @@ -0,0 +1,555 @@ +/* + * Copyright © 2005 Novell, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without + * fee, provided that the above copyright notice appear in all copies + * and that both that copyright notice and this permission notice + * appear in supporting documentation, and that the name of + * Novell, Inc. not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior permission. + * Novell, Inc. makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + * + * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN + * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Author: David Reveman <davidr@novell.com> + */ + +#include <stdlib.h> +#include <string.h> +#include <math.h> + +#include <comp.h> + +ScreenPaintAttrib defaultScreenPaintAttrib = { + 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f +}; + +WindowPaintAttrib defaultWindowPaintAttrib = { + OPAQUE, 0.0f, 0.0f, 1.0f, 1.0f +}; + +void +preparePaintScreen (CompScreen *screen, + int msSinceLastPaint) {} + +void +donePaintScreen (CompScreen *screen) {} + +void +paintTransformedScreen (CompScreen *screen, + const ScreenPaintAttrib *sAttrib, + const WindowPaintAttrib *wAttrib, + unsigned int mask) +{ + CompWindow *w; + int windowMask; + int backgroundMask; + + glPushMatrix (); + + glTranslatef (sAttrib->xTranslate, + sAttrib->yTranslate, + sAttrib->zTranslate - BASE_Z_TRANSLATE); + + glRotatef (sAttrib->xRotate, 0.0f, 1.0f, 0.0f); + glRotatef (sAttrib->vRotate, + 1.0f - sAttrib->xRotate / 90.0f, + 0.0f, + sAttrib->xRotate / 90.0f); + glRotatef (sAttrib->yRotate, 0.0f, 1.0f, 0.0f); + + glTranslatef (-0.5f, -0.5f, 0.5f); + glScalef (1.0f / screen->width, -1.0f / screen->height, 1.0f); + glTranslatef (0.0f, -screen->height, 0.0f); + + if (mask & PAINT_SCREEN_TRANSFORMED_MASK) + { + windowMask = PAINT_WINDOW_ON_TRANSFORMED_SCREEN_MASK; + backgroundMask = PAINT_BACKGROUND_ON_TRANSFORMED_SCREEN_MASK; + + if (mask & PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS_MASK) + { + backgroundMask |= PAINT_BACKGROUND_WITH_STENCIL_MASK; + + (*screen->paintBackground) (screen, &screen->region, + backgroundMask); + + glEnable (GL_STENCIL_TEST); + + for (w = screen->windows; w; w = w->next) + (*screen->paintWindow) (w, wAttrib, &screen->region, + windowMask); + + glDisable (GL_STENCIL_TEST); + + glPopMatrix (); + + return; + } + } + else + windowMask = backgroundMask = 0; + + (*screen->paintBackground) (screen, &screen->region, backgroundMask); + + for (w = screen->windows; w; w = w->next) + (*screen->paintWindow) (w, wAttrib, &screen->region, windowMask); + + glPopMatrix (); +} + +Bool +paintScreen (CompScreen *screen, + const ScreenPaintAttrib *sAttrib, + const WindowPaintAttrib *wAttrib, + Region region, + unsigned int mask) +{ + static Region tmpRegion = NULL; + CompWindow *w; + + if (mask & PAINT_SCREEN_REGION_MASK) + { + if ((mask & PAINT_SCREEN_TRANSFORMED_MASK) || + (mask & PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS_MASK)) + { + if (mask & PAINT_SCREEN_FULL_MASK) + { + (*screen->paintTransformedScreen) (screen, sAttrib, wAttrib, + mask); + + return TRUE; + } + + return FALSE; + } + + /* fall through and redraw region */ + } + else if (mask & PAINT_SCREEN_FULL_MASK) + { + (*screen->paintTransformedScreen) (screen, sAttrib, wAttrib, mask); + + return TRUE; + } + else + return FALSE; + + if (!tmpRegion) + { + tmpRegion = XCreateRegion (); + if (!tmpRegion) + return FALSE; + } + + XSubtractRegion (region, &emptyRegion, tmpRegion); + + glPushMatrix (); + + glTranslatef (0.0f, 0.0f, -BASE_Z_TRANSLATE); + + glTranslatef (-0.5f, -0.5f, 0.5f); + glScalef (1.0f / screen->width, -1.0f / screen->height, 1.0f); + glTranslatef (0.0f, -screen->height, 0.0f); + + /* paint solid windows */ + for (w = screen->reverseWindows; w; w = w->prev) + { + if (w->invisible) + continue; + + if ((*screen->paintWindow) (w, wAttrib, tmpRegion, + PAINT_WINDOW_SOLID_MASK)) + XSubtractRegion (tmpRegion, w->region, tmpRegion); + + /* copy region */ + XSubtractRegion (tmpRegion, &emptyRegion, w->clip); + } + + (*screen->paintBackground) (screen, tmpRegion, 0); + + /* paint translucent windows */ + for (w = screen->windows; w; w = w->next) + { + if (w->invisible) + continue; + + (*screen->paintWindow) (w, wAttrib, w->clip, + PAINT_WINDOW_TRANSLUCENT_MASK); + } + + glPopMatrix (); + + return TRUE; +} + +#define ADD_QUAD(data, w, x1, y1, x2, y2) \ + if (!(w)->pixmap) \ + bindWindow (w); \ + *(data)++ = X_WINDOW_TO_TEXTURE_SPACE (w, x1); \ + *(data)++ = Y_WINDOW_TO_TEXTURE_SPACE (w, y2); \ + *(data)++ = (x1); \ + *(data)++ = (y2); \ + *(data)++ = X_WINDOW_TO_TEXTURE_SPACE (w, x2); \ + *(data)++ = Y_WINDOW_TO_TEXTURE_SPACE (w, y2); \ + *(data)++ = (x2); \ + *(data)++ = (y2); \ + *(data)++ = X_WINDOW_TO_TEXTURE_SPACE (w, x2); \ + *(data)++ = Y_WINDOW_TO_TEXTURE_SPACE (w, y1); \ + *(data)++ = (x2); \ + *(data)++ = (y1); \ + *(data)++ = X_WINDOW_TO_TEXTURE_SPACE (w, x1); \ + *(data)++ = Y_WINDOW_TO_TEXTURE_SPACE (w, y1); \ + *(data)++ = (x1); \ + *(data)++ = (y1) + +#define ADD_BOX(data, w, box) \ + ADD_QUAD (data, w, (box)->x1, (box)->y1, (box)->x2, (box)->y2) + +Bool +paintWindow (CompWindow *w, + const WindowPaintAttrib *attrib, + Region region, + unsigned int mask) +{ + BoxPtr pClip; + int nClip, n; + GLfloat *data, *d; + GLushort opacity; + int x1, y1, x2, y2; + + if (!region->numRects) + return TRUE; + + if (w->destroyed || w->attrib.map_state != IsViewable) + return TRUE; + + if (mask & PAINT_WINDOW_SOLID_MASK) + { + if (w->alpha) + return FALSE; + + opacity = MULTIPLY_USHORT (w->opacity, attrib->opacity); + if (opacity != OPAQUE) + return FALSE; + } + else if (mask & PAINT_WINDOW_TRANSLUCENT_MASK) + { + opacity = MULTIPLY_USHORT (w->opacity, attrib->opacity); + if (!w->alpha && opacity == OPAQUE) + return FALSE; + } + else + { + opacity = MULTIPLY_USHORT (w->opacity, attrib->opacity); + if (w->alpha || opacity != OPAQUE) + mask |= PAINT_WINDOW_TRANSLUCENT_MASK; + else + mask |= PAINT_WINDOW_SOLID_MASK; + } + + if (attrib->xTranslate != 0.0f || + attrib->yTranslate != 0.0f || + attrib->xScale != 1.0f || + attrib->yScale != 1.0f) + { + nClip = w->region->numRects; + pClip = w->region->rects; + + mask |= PAINT_WINDOW_ON_TRANSFORMED_SCREEN_MASK; + + data = malloc (sizeof (GLfloat) * nClip * 16); + if (!data) + return FALSE; + + d = data; + + n = nClip; + while (nClip--) + { + x1 = pClip->x1 - w->attrib.x; + y1 = pClip->y1 - w->attrib.y; + x2 = pClip->x2 - w->attrib.x; + y2 = pClip->y2 - w->attrib.y; + + ADD_QUAD (d, w, x1, y1, x2, y2); + + pClip++; + } + } + else + { + BoxRec clip, full; + BoxPtr pExtent = ®ion->extents; + BoxPtr pBox = region->rects; + int nBox = region->numRects; + int dataSize; + + full.x1 = 0; + full.y1 = 0; + full.x2 = w->width; + full.y2 = w->height; + + x1 = pExtent->x1 - w->attrib.x; + y1 = pExtent->y1 - w->attrib.y; + x2 = pExtent->x2 - w->attrib.x; + y2 = pExtent->y2 - w->attrib.y; + + if (x1 > 0) + full.x1 = x1; + if (y1 > 0) + full.y1 = y1; + if (x2 < w->width) + full.x2 = x2; + if (y2 < w->height) + full.y2 = y2; + + if (full.x1 >= full.x2 || full.y1 >= full.y2) + return TRUE; + + dataSize = nBox * 16; + data = malloc (sizeof (GLfloat) * dataSize); + if (!data) + return FALSE; + + d = data; + n = 0; + + pBox = region->rects; + nBox = region->numRects; + while (nBox--) + { + x1 = pBox->x1 - w->attrib.x; + y1 = pBox->y1 - w->attrib.y; + x2 = pBox->x2 - w->attrib.x; + y2 = pBox->y2 - w->attrib.y; + + pBox++; + + if (x1 < full.x1) + x1 = full.x1; + if (y1 < full.y1) + y1 = full.y1; + if (x2 > full.x2) + x2 = full.x2; + if (y2 > full.y2) + y2 = full.y2; + + if (x1 < x2 && y1 < y2) + { + nClip = w->region->numRects; + + if (nClip == 1) + { + ADD_QUAD (d, w, x1, y1, x2, y2); + + n++; + } + else + { + pClip = w->region->rects; + + while (nClip--) + { + clip.x1 = pClip->x1 - w->attrib.x; + clip.y1 = pClip->y1 - w->attrib.y; + clip.x2 = pClip->x2 - w->attrib.x; + clip.y2 = pClip->y2 - w->attrib.y; + + pClip++; + + if (clip.x1 < x1) + clip.x1 = x1; + if (clip.y1 < y1) + clip.y1 = y1; + if (clip.x2 > x2) + clip.x2 = x2; + if (clip.y2 > y2) + clip.y2 = y2; + + if (clip.x1 < clip.x2 && clip.y1 < clip.y2) + { + if ((n << 4) == dataSize) + { + dataSize <<= 2; + data = realloc (data, + sizeof (GLfloat) * dataSize); + if (!data) + return FALSE; + + d = data + (n * 16); + } + + ADD_BOX (d, w, &clip); + + n++; + } + } + } + } + } + } + + if (n) + { + glTexCoordPointer (2, GL_FLOAT, sizeof (GLfloat) * 4, data); + glVertexPointer (2, GL_FLOAT, sizeof (GLfloat) * 4, data + 2); + + if (mask & PAINT_WINDOW_TRANSLUCENT_MASK) + { + glEnable (GL_BLEND); + if (opacity != OPAQUE) + { + glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glColor4us (opacity, opacity, opacity, opacity); + } + } + + glPushMatrix (); + + if (mask & PAINT_WINDOW_ON_TRANSFORMED_SCREEN_MASK) + { + glTranslatef (w->attrib.x + attrib->xTranslate, + w->attrib.y + attrib->yTranslate, 0.0f); + glScalef (attrib->xScale, attrib->yScale, 0.0f); + + enableTexture (w->screen, &w->texture, COMP_TEXTURE_FILTER_GOOD); + } + else + { + glTranslatef (w->attrib.x, w->attrib.y, 0.0f); + + enableTexture (w->screen, &w->texture, COMP_TEXTURE_FILTER_FAST); + } + + glDrawArrays (GL_QUADS, 0, n * 4); + + disableTexture (&w->texture); + + glPopMatrix (); + + if (mask & PAINT_WINDOW_TRANSLUCENT_MASK) + { + if (opacity != OPAQUE) + { + glColor4usv (defaultColor); + glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + } + glDisable (GL_BLEND); + } + } + + free (data); + + return TRUE; +} + +void +paintBackground (CompScreen *s, + Region region, + unsigned int mask) +{ + CompTexture *bg = &s->backgroundTexture; + BoxPtr pBox = region->rects; + int n, nBox = region->numRects; + GLfloat *d, *data; + + if (!nBox) + return; + + if (s->desktopWindowCount) + { + if (bg->name) + { + finiTexture (s, bg); + initTexture (s, bg); + } + + if (!(mask & PAINT_BACKGROUND_WITH_STENCIL_MASK)) + return; + } + else + { + if (!bg->name) + updateScreenBackground (s, bg); + } + + data = malloc (sizeof (GLfloat) * nBox * 16); + if (!data) + return; + + d = data; + n = nBox; + while (n--) + { + *d++ = bg->dx * pBox->x1; + *d++ = bg->dy * (s->backgroundHeight - pBox->y2); + + *d++ = pBox->x1; + *d++ = pBox->y2; + + *d++ = bg->dx * pBox->x2; + *d++ = bg->dy * (s->backgroundHeight - pBox->y2); + + *d++ = pBox->x2; + *d++ = pBox->y2; + + *d++ = bg->dx * pBox->x2; + *d++ = bg->dy * (s->backgroundHeight - pBox->y1); + + *d++ = pBox->x2; + *d++ = pBox->y1; + + *d++ = bg->dx * pBox->x1; + *d++ = bg->dy * (s->backgroundHeight - pBox->y1); + + *d++ = pBox->x1; + *d++ = pBox->y1; + + pBox++; + } + + if (mask & PAINT_BACKGROUND_WITH_STENCIL_MASK) + { + glEnable (GL_STENCIL_TEST); + glStencilFunc (GL_ALWAYS, s->stencilRef, ~0); + glStencilOp (GL_REPLACE, GL_REPLACE, GL_REPLACE); + } + + glTexCoordPointer (2, GL_FLOAT, sizeof (GLfloat) * 4, data); + glVertexPointer (2, GL_FLOAT, sizeof (GLfloat) * 4, data + 2); + + if (s->desktopWindowCount) + { + glDrawArrays (GL_QUADS, 0, nBox * 4); + } + else + { + if (mask & PAINT_BACKGROUND_ON_TRANSFORMED_SCREEN_MASK) + enableTexture (s, bg, COMP_TEXTURE_FILTER_GOOD); + else + enableTexture (s, bg, COMP_TEXTURE_FILTER_FAST); + + glDrawArrays (GL_QUADS, 0, nBox * 4); + + disableTexture (bg); + } + + if (mask & PAINT_BACKGROUND_WITH_STENCIL_MASK) + { + glStencilFunc (GL_EQUAL, s->stencilRef, ~0); + glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP); + glDisable (GL_STENCIL_TEST); + } + + free (data); +} diff --git a/src/plugin.c b/src/plugin.c new file mode 100644 index 0000000..b679070 --- /dev/null +++ b/src/plugin.c @@ -0,0 +1,358 @@ +/* + * Copyright © 2005 Novell, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without + * fee, provided that the above copyright notice appear in all copies + * and that both that copyright notice and this permission notice + * appear in supporting documentation, and that the name of + * Novell, Inc. not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior permission. + * Novell, Inc. makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + * + * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN + * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Author: David Reveman <davidr@novell.com> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <dlfcn.h> + +#include <comp.h> + +#define HOME_PLUGINDIR ".glxcomp/plugins" + +CompPlugin *plugins = 0; + +static Bool +initPlugin (CompPlugin *p) +{ + CompDisplay *d = compDisplays; + int failed = 0; + + if (!(*p->vTable->init) (p)) + { + fprintf (stderr, "%s: InitPlugin '%s' failed\n", programName, + p->vTable->name); + return FALSE; + } + + if (d) + { + if ((*d->initPluginForDisplay) (p, d)) + { + CompScreen *s, *failedScreen = d->screens; + + for (s = d->screens; s; s = s->next) + { + if (!p->vTable->initScreen || (*s->initPluginForScreen) (p, s)) + { + CompWindow *w, *failedWindow = s->windows; + + for (w = s->windows; w; w = w->next) + { + if (p->vTable->initWindow && + !(*p->vTable->initWindow) (p, w)) + { + fprintf (stderr, "%s: Plugin '%s':initWindow " + "failed\n", programName, p->vTable->name); + failedWindow = w; + failed = 1; + break; + } + } + + for (w = s->windows; w != failedWindow; w = w->next) + { + if (p->vTable->finiWindow) + (*p->vTable->finiWindow) (p, w); + } + } + else + { + fprintf (stderr, "%s: Plugin '%s':initScreen failed\n", + programName, p->vTable->name); + failedScreen = s; + failed = 1; + break; + } + } + + for (s = d->screens; s != failedScreen; s = s->next) + (*s->finiPluginForScreen) (p, s); + } + else + { + fprintf (stderr, "%s: Plugin '%s':initDisplay failed\n", + programName, p->vTable->name); + + failed = 1; + (*d->finiPluginForDisplay) (p, d); + } + } + + if (failed) + { + (*p->vTable->fini) (p); + + return FALSE; + } + + return TRUE; +} + +static void +finiPlugin (CompPlugin *p) +{ + CompDisplay *d = compDisplays; + CompScreen *s; + + if (d) + { + for (s = d->screens; s; s = s->next) + { + CompWindow *w = s->windows; + + if (p->vTable->finiWindow) + { + for (w = s->windows; w; w = w->next) + (*p->vTable->finiWindow) (p, w); + } + + (*s->finiPluginForScreen) (p, s); + } + + (*d->finiPluginForDisplay) (p, d); + } + + (*p->vTable->fini) (p); +} + +void +screenInitPlugins (CompScreen *s) +{ + CompPlugin *p; + int i, j = 0; + + for (p = plugins; p; p = p->next) + j++; + + while (j--) + { + i = 0; + for (p = plugins; i < j; p = p->next) + i++; + + if (p->vTable->initScreen) + (*s->initPluginForScreen) (p, s); + } +} + +void +screenFiniPlugins (CompScreen *s) +{ + CompPlugin *p; + + for (p = plugins; p; p = p->next) + { + if (p->vTable->finiScreen) + (*s->initPluginForScreen) (p, s); + } +} + +void +windowInitPlugins (CompWindow *w) +{ + CompPlugin *p; + + for (p = plugins; p; p = p->next) + { + if (p->vTable->initWindow) + (*p->vTable->initWindow) (p, w); + } +} + +void +windowFiniPlugins (CompWindow *w) +{ + CompPlugin *p; + + for (p = plugins; p; p = p->next) + { + if (p->vTable->finiWindow) + (*p->vTable->finiWindow) (p, w); + } +} + +CompPlugin * +findActivePlugin (char *name) +{ + CompPlugin *p; + + for (p = plugins; p; p = p->next) + { + if (strcmp (p->vTable->name, name) == 0) + return p; + } + + return 0; +} + +CompPlugin * +loadPlugin (char *name) +{ + CompPlugin *p; + char *file; + + p = malloc (sizeof (CompPlugin)); + if (!p) + return 0; + + file = malloc (strlen (name) + 7); + if (!file) + { + free (p); + return 0; + } + + sprintf (file, "lib%s.so", name); + + p->next = 0; + p->dlhand = 0; + p->vTable = 0; + + p->dlhand = dlopen (file, RTLD_LAZY); + if (!p->dlhand) + { + char *home, *plugindir; + + home = getenv ("HOME"); + if (home) + { + plugindir = malloc (strlen (home) + + strlen (HOME_PLUGINDIR) + + strlen (file) + 3); + if (plugindir) + { + sprintf (plugindir, "%s/%s/%s", home, HOME_PLUGINDIR, file); + p->dlhand = dlopen (plugindir, RTLD_LAZY); + free (plugindir); + } + } + + if (!p->dlhand) + { + plugindir = malloc (strlen (PLUGINDIR) + strlen (file) + 2); + if (plugindir) + { + sprintf (plugindir, "%s/%s", PLUGINDIR, file); + p->dlhand = dlopen (plugindir, RTLD_LAZY); + free (plugindir); + } + } + } + + if (p->dlhand) + { + PluginGetInfoProc getInfo; + + dlerror (); + + getInfo = (PluginGetInfoProc) dlsym (p->dlhand, "getCompPluginInfo"); + + if (dlerror () != 0) + getInfo = 0; + + if (getInfo) + { + p->vTable = (*getInfo) (); + if (!p->vTable) + { + fprintf (stderr, "%s: Couldn't get vtable from '%s' plugin\n", + programName, file); + + dlclose (p->dlhand); + free (p); + p = 0; + } + } + else + { + fprintf (stderr, "%s: Failed to lookup getCompPluginInfo in '%s' " + "plugin\n", programName, file); + + dlclose (p->dlhand); + free (p); + p = 0; + } + } + else + { + fprintf (stderr, "%s: Couldn't load plugin '%s'\n", programName, + file); + free (p); + p = 0; + } + + free (file); + + return p; +} + +void +unloadPlugin (CompPlugin *p) +{ + dlclose (p->dlhand); + free (p); +} + +Bool +pushPlugin (CompPlugin *p) +{ + if (findActivePlugin (p->vTable->name)) + { + fprintf (stderr, "%s: Plugin '%s' already active\n", programName, + p->vTable->name); + + return FALSE; + } + + p->next = plugins; + plugins = p; + + if (!initPlugin (p)) + { + fprintf (stderr, "%s: Couldn't activate plugin '%s'\n", programName, + p->vTable->name); + plugins = p->next; + + return FALSE; + } + + return TRUE; +} + +CompPlugin * +popPlugin (void) +{ + CompPlugin *p = plugins; + + if (!p) + return 0; + + finiPlugin (p); + + plugins = p->next; + + return p; +} diff --git a/src/privates.c b/src/privates.c new file mode 100644 index 0000000..b004452 --- /dev/null +++ b/src/privates.c @@ -0,0 +1,68 @@ +/* + * Copyright © 2005 Novell, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without + * fee, provided that the above copyright notice appear in all copies + * and that both that copyright notice and this permission notice + * appear in supporting documentation, and that the name of + * Novell, Inc. not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior permission. + * Novell, Inc. makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + * + * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN + * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Author: David Reveman <davidr@novell.com> + */ + +#include <stdlib.h> + +#include <comp.h> + +int +allocatePrivateIndex (int *len, + char **indices, + ReallocPrivatesProc reallocProc, + void *closure) +{ + char *newIndices; + int i; + + for (i = 0; i < *len; i++) + { + if (!(*indices)[i]) + { + (*indices)[i] = 1; + return i; + } + } + + newIndices = (char *) realloc (*indices, (*len + 1) * sizeof (char)); + if (!newIndices) + return -1; + + newIndices[*len] = 1; + *indices = newIndices; + + if (!(*reallocProc) (*len + 1, closure)) + return -1; + + return (*len)++; +} + +void +freePrivateIndex (int len, + char *indices, + int index) +{ + if (index < len) + indices[index] = 0; +} diff --git a/src/readpng.c b/src/readpng.c new file mode 100644 index 0000000..a9e1661 --- /dev/null +++ b/src/readpng.c @@ -0,0 +1,199 @@ +/* + * Copyright © 2005 Novell, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without + * fee, provided that the above copyright notice appear in all copies + * and that both that copyright notice and this permission notice + * appear in supporting documentation, and that the name of + * Novell, Inc. not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior permission. + * Novell, Inc. makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + * + * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN + * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Author: David Reveman <davidr@novell.com> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <png.h> +#include <setjmp.h> + +#include <comp.h> + +#define HOME_IMAGEDIR ".glxcomp/images" + +static void +premultiplyData (png_structp png, + png_row_infop row_info, + png_bytep data) +{ + unsigned int i; + + for (i = 0; i < row_info->rowbytes; i += 4) + { + unsigned char *base = &data[i]; + unsigned char blue = base[0]; + unsigned char green = base[1]; + unsigned char red = base[2]; + unsigned char alpha = base[3]; + int p; + + red = (unsigned) red * (unsigned) alpha / 255; + green = (unsigned) green * (unsigned) alpha / 255; + blue = (unsigned) blue * (unsigned) alpha / 255; + + p = (alpha << 24) | (red << 16) | (green << 8) | (blue << 0); + memcpy (base, &p, sizeof (int)); + } +} + +Bool +readPng (const char *filename, + char **data, + unsigned int *width, + unsigned int *height) +{ + static const int PNG_SIG_SIZE = 8; + unsigned char png_sig[PNG_SIG_SIZE]; + FILE *file; + int sig_bytes; + png_struct *png; + png_info *info; + png_uint_32 png_width, png_height; + int depth, color_type, interlace, i; + unsigned int pixel_size; + png_byte **row_pointers; + + file = fopen (filename, "r"); + if (!file) + { + char *home, *imagedir; + + home = getenv ("HOME"); + if (home) + { + imagedir = malloc (strlen (home) + + strlen (HOME_IMAGEDIR) + + strlen (filename) + 3); + if (imagedir) + { + sprintf (imagedir, "%s/%s/%s", home, HOME_IMAGEDIR, filename); + file = fopen (imagedir, "r"); + free (imagedir); + } + } + + if (!file) + { + imagedir = malloc (strlen (IMAGEDIR) + strlen (filename) + 2); + if (imagedir) + { + sprintf (imagedir, "%s/%s", IMAGEDIR, filename); + file = fopen (imagedir, "r"); + free (imagedir); + } + + if (!file) + return FALSE; + } + } + + sig_bytes = fread (png_sig, 1, PNG_SIG_SIZE, file); + if (png_check_sig (png_sig, sig_bytes) == 0) + { + fclose (file); + return FALSE; + } + + png = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png) + { + fclose (file); + return FALSE; + } + + info = png_create_info_struct (png); + if (info == NULL) + { + fclose (file); + png_destroy_read_struct (&png, NULL, NULL); + return FALSE; + } + + png_init_io (png, file); + png_set_sig_bytes (png, sig_bytes); + + png_read_info (png, info); + + png_get_IHDR (png, info, + &png_width, &png_height, &depth, + &color_type, &interlace, NULL, NULL); + *width = png_width; + *height = png_height; + + /* convert palette/gray image to rgb */ + if (color_type == PNG_COLOR_TYPE_PALETTE) + png_set_palette_to_rgb (png); + + /* expand gray bit depth if needed */ + if (color_type == PNG_COLOR_TYPE_GRAY && depth < 8) + png_set_gray_1_2_4_to_8 (png); + + /* transform transparency to alpha */ + if (png_get_valid(png, info, PNG_INFO_tRNS)) + png_set_tRNS_to_alpha (png); + + if (depth == 16) + png_set_strip_16 (png); + + if (depth < 8) + png_set_packing (png); + + /* convert grayscale to RGB */ + if (color_type == PNG_COLOR_TYPE_GRAY || + color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_gray_to_rgb (png); + + if (interlace != PNG_INTERLACE_NONE) + png_set_interlace_handling (png); + + png_set_bgr (png); + png_set_filler (png, 0xff, PNG_FILLER_AFTER); + + png_set_read_user_transform_fn (png, premultiplyData); + + png_read_update_info (png, info); + + pixel_size = 4; + *data = (char *) malloc (png_width * png_height * pixel_size); + if (*data == NULL) + { + fclose (file); + return FALSE; + } + + row_pointers = (png_byte **) malloc (png_height * sizeof (char *)); + for (i=0; i < png_height; i++) + row_pointers[i] = (png_byte *) (*data + i * png_width * pixel_size); + + png_read_image (png, row_pointers); + png_read_end (png, info); + + free (row_pointers); + fclose (file); + + png_destroy_read_struct (&png, &info, NULL); + + return TRUE; +} diff --git a/src/screen.c b/src/screen.c new file mode 100644 index 0000000..3664961 --- /dev/null +++ b/src/screen.c @@ -0,0 +1,1294 @@ +/* + * Copyright © 2005 Novell, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without + * fee, provided that the above copyright notice appear in all copies + * and that both that copyright notice and this permission notice + * appear in supporting documentation, and that the name of + * Novell, Inc. not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior permission. + * Novell, Inc. makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + * + * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN + * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Author: David Reveman <davidr@novell.com> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <dlfcn.h> +#include <string.h> + +#include <comp.h> + +#define NUM_OPTIONS(s) (sizeof ((s)->opt) / sizeof (CompOption)) + +static int +reallocScreenPrivate (int size, + void *closure) +{ + CompDisplay *d = (CompDisplay *) closure; + CompScreen *s; + void *privates; + + for (s = d->screens; s; s = s->next) + { + privates = realloc (s->privates, size * sizeof (CompPrivate)); + if (!privates) + return FALSE; + + s->privates = (CompPrivate *) privates; + } + + return TRUE; +} + +int +allocateScreenPrivateIndex (CompDisplay *display) +{ + return allocatePrivateIndex (&display->screenPrivateLen, + &display->screenPrivateIndices, + reallocScreenPrivate, + (void *) display); +} + +void +freeScreenPrivateIndex (CompDisplay *display, + int index) +{ + freePrivateIndex (display->screenPrivateLen, + display->screenPrivateIndices, + index); +} + +CompOption * +compGetScreenOptions (CompScreen *screen, + int *count) +{ + *count = NUM_OPTIONS (screen); + return screen->opt; +} + +static Bool +setScreenOption (CompScreen *screen, + char *name, + CompOptionValue *value) +{ + CompOption *o; + int index; + + o = compFindOption (screen->opt, NUM_OPTIONS (screen), name, &index); + if (!o) + return FALSE; + + switch (index) { + case COMP_SCREEN_OPTION_REFRESH_RATE: + if (compSetIntOption (o, value)) + { + screen->redrawTime = 1000 / o->value.i; + return TRUE; + } + default: + break; + } + + return FALSE; +} + +static Bool +setScreenOptionForPlugin (CompScreen *screen, + char *plugin, + char *name, + CompOptionValue *value) +{ + CompPlugin *p; + + p = findActivePlugin (plugin); + if (p && p->vTable->setScreenOption) + return (*p->vTable->setScreenOption) (screen, name, value); + + return FALSE; +} + +static void +compScreenInitOptions (CompScreen *screen) +{ + CompOption *o; + + o = &screen->opt[COMP_SCREEN_OPTION_REFRESH_RATE]; + o->name = "refresh_rate"; + o->shortDesc = "Refresh Rate"; + o->longDesc = "The rate at which the screen is redrawn (times/second)"; + o->type = CompOptionTypeInt; + o->value.i = defaultRefreshRate; + o->rest.i.min = 1; + o->rest.i.max = 200; +} + +static Bool +initPluginForScreen (CompPlugin *p, + CompScreen *s) +{ + if (p->vTable->initScreen) + return (*p->vTable->initScreen) (p, s); + + return FALSE; +} + +static void +finiPluginForScreen (CompPlugin *p, + CompScreen *s) +{ + if (p->vTable->finiScreen) + (*p->vTable->finiScreen) (p, s); +} + +static void +frustum (GLfloat left, + GLfloat right, + GLfloat bottom, + GLfloat top, + GLfloat nearval, + GLfloat farval) +{ + GLfloat x, y, a, b, c, d; + GLfloat m[16]; + + x = (2.0 * nearval) / (right - left); + y = (2.0 * nearval) / (top - bottom); + a = (right + left) / (right - left); + b = (top + bottom) / (top - bottom); + c = -(farval + nearval) / ( farval - nearval); + d = -(2.0 * farval * nearval) / (farval - nearval); + +#define M(row,col) m[col*4+row] + M(0,0) = x; M(0,1) = 0.0F; M(0,2) = a; M(0,3) = 0.0F; + M(1,0) = 0.0F; M(1,1) = y; M(1,2) = b; M(1,3) = 0.0F; + M(2,0) = 0.0F; M(2,1) = 0.0F; M(2,2) = c; M(2,3) = d; + M(3,0) = 0.0F; M(3,1) = 0.0F; M(3,2) = -1.0F; M(3,3) = 0.0F; +#undef M + + glMultMatrixf (m); +} + +static void +perspective (GLfloat fovy, + GLfloat aspect, + GLfloat zNear, + GLfloat zFar) +{ + GLfloat xmin, xmax, ymin, ymax; + + ymax = zNear * tan (fovy * M_PI / 360.0); + ymin = -ymax; + xmin = ymin * aspect; + xmax = ymax * aspect; + + frustum (xmin, xmax, ymin, ymax, zNear, zFar); +} + +static void +reshape (CompScreen *s, + int w, + int h) +{ + s->width = w; + s->height = h; + + glViewport (0, 0, w, h); + glMatrixMode (GL_PROJECTION); + glLoadIdentity (); + perspective (60.0f, 1.0f, 0.1f, 100.0f); + glMatrixMode (GL_MODELVIEW); + + s->region.rects = &s->region.extents; + s->region.numRects = 1; + s->region.extents.x1 = 0; + s->region.extents.y1 = 0; + s->region.extents.x2 = w; + s->region.extents.y2 = h; + s->region.size = 1; +} + +void +configureScreen (CompScreen *s, + XConfigureEvent *ce) +{ + if (s->attrib.width != ce->width || + s->attrib.height != ce->height) + { + s->attrib.width = ce->width; + s->attrib.height = ce->height; + + reshape (s, ce->width, ce->height); + + damageScreen (s); + } +} + +static FuncPtr +getProcAddress (CompScreen *s, + const char *name) +{ + static void *dlhand = NULL; + FuncPtr funcPtr = NULL; + + if (s->getProcAddress) + funcPtr = s->getProcAddress ((GLubyte *) name); + + if (!funcPtr) + { + if (!dlhand) + dlhand = dlopen (NULL, RTLD_LAZY); + + if (dlhand) + { + dlerror (); + funcPtr = (FuncPtr) dlsym (dlhand, name); + if (dlerror () != NULL) + funcPtr = NULL; + } + } + + return funcPtr; +} + +void +updateScreenBackground (CompScreen *screen, + CompTexture *texture) +{ + Display *dpy = screen->display->display; + Atom pixmapAtom, actualType; + int actualFormat, i, status; + unsigned int width = 1, height = 1, depth = 0; + unsigned long nItems; + unsigned long bytesAfter; + unsigned char *prop; + Pixmap pixmap = 0; + + pixmapAtom = XInternAtom (dpy, "PIXMAP", FALSE); + + for (i = 0; pixmap == 0 && i < 2; i++) + { + status = XGetWindowProperty (dpy, screen->root, + screen->display->xBackgroundAtom[i], + 0, 4, FALSE, AnyPropertyType, + &actualType, &actualFormat, &nItems, + &bytesAfter, &prop); + + if (status == Success && nItems && prop) + { + if (actualType == pixmapAtom && + actualFormat == 32 && + nItems == 1) + { + Pixmap p; + + memcpy (&p, prop, 4); + + if (p) + { + unsigned int ui; + int i; + Window w; + + if (XGetGeometry (dpy, p, &w, &i, &i, + &width, &height, &ui, &depth)) + { + if (depth == screen->attrib.depth) + pixmap = p; + } + } + } + + XFree (prop); + } + } + + if (!testMode && pixmap) + { + if (pixmap == texture->pixmap) + return; + + finiTexture (screen, texture); + initTexture (screen, texture); + + if (!bindPixmapToTexture (screen, texture, pixmap, + width, height, depth)) + { + fprintf (stderr, "%s: Couldn't bind background pixmap 0x%x to " + "texture\n", programName, (int) pixmap); + } + } + else + { + finiTexture (screen, texture); + initTexture (screen, texture); + } + + if (!texture->name) + readImageToTexture (screen, texture, backgroundImage, &width, &height); + + if (texture->target == GL_TEXTURE_2D) + { + glBindTexture (texture->target, texture->name); + glTexParameteri (texture->target, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri (texture->target, GL_TEXTURE_WRAP_T, GL_REPEAT); + glBindTexture (texture->target, 0); + } + + screen->backgroundWidth = width; + screen->backgroundHeight = height; +} + +Bool +addScreen (CompDisplay *display, + int screenNum) +{ + CompScreen *s; + Display *dpy = display->display; + static char data = 0; + XColor black, dummy; + Pixmap bitmap; + XVisualInfo templ; + XVisualInfo *visinfo; + VisualID visualIDs[MAX_DEPTH + 1]; + Window rootReturn, parentReturn; + Window *children; + unsigned int nchildren; + int defaultDepth, nvisinfo, value, i; + const char *glxExtensions, *glExtensions; + GLint stencilBits; + + s = malloc (sizeof (CompScreen)); + if (!s) + return FALSE; + + s->windowPrivateIndices = 0; + s->windowPrivateLen = 0; + + if (display->screenPrivateLen) + { + s->privates = malloc (display->screenPrivateLen * + sizeof (CompPrivate)); + if (!s->privates) + { + free (s); + return FALSE; + } + } + else + s->privates = 0; + + compScreenInitOptions (s); + + s->redrawTime = 1000 / s->opt[COMP_SCREEN_OPTION_REFRESH_RATE].value.i; + + s->display = display; + + s->damage = XCreateRegion (); + if (!s->damage) + return FALSE; + + s->buttonGrab = 0; + s->nButtonGrab = 0; + s->keyGrab = 0; + s->nKeyGrab = 0; + + s->grabs = 0; + s->grabSize = 0; + s->maxGrab = 0; + + s->pendingDestroys = 0; + + s->screenNum = screenNum; + s->colormap = DefaultColormap (dpy, screenNum); + s->root = XRootWindow (dpy, screenNum); + + if (testMode) + { + XSetWindowAttributes attrib; + XWMHints *wmHints; + XSizeHints *normalHints; + XClassHint *classHint; + int glx_attrib[] = { + GLX_RGBA, + GLX_RED_SIZE, 1, + GLX_STENCIL_SIZE, 2, + GLX_DOUBLEBUFFER, + None + }; + + visinfo = glXChooseVisual (dpy, screenNum, glx_attrib); + if (!visinfo) + { + int glx_attrib2[] = { + GLX_RGBA, + GLX_RED_SIZE, 1, + GLX_DOUBLEBUFFER, + None + }; + + visinfo = glXChooseVisual (dpy, screenNum, glx_attrib2); + if (!visinfo) + { + fprintf (stderr, "%s: Couldn't find a double buffered " + "RGB visual.\n", programName); + return FALSE; + } + } + + attrib.colormap = XCreateColormap (dpy, s->root, visinfo->visual, + AllocNone); + + normalHints = XAllocSizeHints (); + normalHints->flags = 0; + normalHints->x = 0; + normalHints->y = 0; + normalHints->width = 800; + normalHints->height = 600; + + classHint = XAllocClassHint (); + classHint->res_name = "glxcompmgr"; + classHint->res_class = "Glxcompmgr"; + + wmHints = XAllocWMHints (); + wmHints->flags = InputHint; + wmHints->input = TRUE; + + s->root = XCreateWindow (dpy, s->root, 0, 0, + normalHints->width, normalHints->height, 0, + visinfo->depth, InputOutput, visinfo->visual, + CWColormap, &attrib); + + XSetWMProtocols (dpy, s->root, &display->wmDeleteWindowAtom, 1); + + XmbSetWMProperties (dpy, s->root, + "glxcompmgr - Test mode", "glxcompmgr", + programArgv, programArgc, + normalHints, wmHints, classHint); + + s->fake[0] = XCreateWindow (dpy, s->root, 64, 32, 1, 1, 0, + visinfo->depth, InputOutput, + visinfo->visual, + CWColormap, &attrib); + + s->fake[1] = XCreateWindow (dpy, s->root, 256, 256, 1, 1, 0, + visinfo->depth, InputOutput, + visinfo->visual, + CWColormap, &attrib); + + XMapWindow (dpy, s->root); + + XFree (wmHints); + XFree (classHint); + XFree (normalHints); + } + else + s->fake[0] = s->fake[1] = 0; + + s->escapeKeyCode = XKeysymToKeycode (display->display, + XStringToKeysym ("Escape")); + + s->allDamaged = TRUE; + s->next = 0; + s->exposeRects = 0; + s->sizeExpose = 0; + s->nExpose = 0; + + s->rasterX = 0; + s->rasterY = 0; + + s->windows = 0; + s->reverseWindows = 0; + + s->stencilRef = 0x1; + + s->nextRedraw = 0; + + gettimeofday (&s->lastRedraw, 0); + + s->setScreenOption = setScreenOption; + s->setScreenOptionForPlugin = setScreenOptionForPlugin; + + s->initPluginForScreen = initPluginForScreen; + s->finiPluginForScreen = finiPluginForScreen; + + s->preparePaintScreen = preparePaintScreen; + s->donePaintScreen = donePaintScreen; + s->paintScreen = paintScreen; + s->paintTransformedScreen = paintTransformedScreen; + s->paintBackground = paintBackground; + s->paintWindow = paintWindow; + s->invisibleWindowMove = invisibleWindowMove; + + s->getProcAddress = 0; + + if (s->root) + { + XSetWindowAttributes attrib; + + attrib.override_redirect = 1; + s->grabWindow = XCreateWindow (dpy, s->root, -100, -100, 1, 1, 0, + CopyFromParent, CopyFromParent, + CopyFromParent, CWOverrideRedirect, + &attrib); + + XMapWindow (dpy, s->grabWindow); + } + + if (!XGetWindowAttributes (dpy, s->root, &s->attrib)) + return FALSE; + + s->activeWindow = None; + + templ.visualid = XVisualIDFromVisual (s->attrib.visual); + + visinfo = XGetVisualInfo (dpy, VisualIDMask, &templ, &nvisinfo); + if (!nvisinfo) + { + fprintf (stderr, "%s: Couldn't get visual info for default visual\n", + programName); + return FALSE; + } + + defaultDepth = visinfo->depth; + + if (!XAllocNamedColor (dpy, s->colormap, "black", &black, &dummy)) + { + fprintf (stderr, "%s: Couldn't allocate color\n", programName); + return FALSE; + } + + bitmap = XCreateBitmapFromData (dpy, s->root, &data, 1, 1); + if (!bitmap) + { + fprintf (stderr, "%s: Couldn't create bitmap\n", programName); + return FALSE; + } + + s->invisibleCursor = XCreatePixmapCursor (dpy, bitmap, bitmap, + &black, &black, 0, 0); + if (!s->invisibleCursor) + { + fprintf (stderr, "%s: Couldn't create invisible cursor\n", + programName); + return FALSE; + } + + XFreePixmap (dpy, bitmap); + XFreeColors (dpy, s->colormap, &black.pixel, 1, 0); + + glXGetConfig (dpy, visinfo, GLX_USE_GL, &value); + if (!value) + { + fprintf (stderr, "%s: Root visual is not a GL visual\n", + programName); + return FALSE; + } + + glXGetConfig (dpy, visinfo, GLX_DOUBLEBUFFER, &value); + if (!value) + { + fprintf (stderr, + "%s: Root visual is not a double buffered GL visual\n", + programName); + return FALSE; + } + + s->ctx = glXCreateContext (dpy, visinfo, NULL, TRUE); + if (!s->ctx) + { + fprintf (stderr, "%s: glXCreateContext failed\n", programName); + return FALSE; + } + + XFree (visinfo); + + /* we don't want to allocate back, stencil or depth buffers for pixmaps + so lets see if we can find an approriate visual without these buffers */ + for (i = 0; i <= MAX_DEPTH; i++) + { + int j, db, stencil, depth; + + visualIDs[i] = 0; + + db = MAXSHORT; + stencil = MAXSHORT; + depth = MAXSHORT; + + templ.depth = i; + + visinfo = XGetVisualInfo (dpy, VisualDepthMask, &templ, &nvisinfo); + for (j = 0; j < nvisinfo; j++) + { + glXGetConfig (dpy, &visinfo[j], GLX_USE_GL, &value); + if (!value) + continue; + + glXGetConfig (dpy, &visinfo[j], GLX_DOUBLEBUFFER, &value); + if (value > db) + continue; + + db = value; + glXGetConfig (dpy, &visinfo[j], GLX_STENCIL_SIZE, &value); + if (value > stencil) + continue; + + stencil = value; + glXGetConfig (dpy, &visinfo[j], GLX_DEPTH_SIZE, &value); + if (value > depth) + continue; + + depth = value; + visualIDs[i] = visinfo[j].visualid; + } + + if (nvisinfo) + XFree (visinfo); + } + + /* create contexts for supported depths */ + for (i = 0; i <= MAX_DEPTH; i++) + { + templ.visualid = visualIDs[i]; + s->glxPixmapVisuals[i] = XGetVisualInfo (dpy, + VisualIDMask, + &templ, + &nvisinfo); + } + + if (!s->glxPixmapVisuals[defaultDepth]) + { + fprintf (stderr, "%s: No GL visual for default depth, " + "this isn't going to work.\n", programName); + return FALSE; + } + + glXMakeCurrent (dpy, s->root, s->ctx); + currentRoot = s->root; + + glxExtensions = glXQueryExtensionsString (s->display->display, screenNum); + if (!testMode && !strstr (glxExtensions, "GLX_MESA_render_texture")) + { + fprintf (stderr, "%s: GLX_MESA_render_texture is missing\n", + programName); + return FALSE; + } + + s->getProcAddress = (GLXGetProcAddressProc) + getProcAddress (s, "glXGetProcAddressARB"); + s->bindTexImage = (GLXBindTexImageProc) + getProcAddress (s, "glXBindTexImageMESA"); + s->releaseTexImage = (GLXReleaseTexImageProc) + getProcAddress (s, "glXReleaseTexImageMESA"); + s->queryDrawable = (GLXQueryDrawableProc) + getProcAddress (s, "glXQueryDrawable"); + + if (!testMode && !s->bindTexImage) + { + fprintf (stderr, "%s: glXBindTexImageMESA is missing\n", programName); + return FALSE; + } + + if (!testMode && !s->releaseTexImage) + { + fprintf (stderr, "%s: glXReleaseTexImageMESA is missing\n", + programName); + return FALSE; + } + + if (!testMode && !s->queryDrawable) + { + fprintf (stderr, "%s: glXQueryDrawable is missing\n", programName); + return FALSE; + } + + glExtensions = (const char *) glGetString (GL_EXTENSIONS); + if (strstr (glExtensions, "GL_NV_texture_rectangle") || + strstr (glExtensions, "GL_EXT_texture_rectangle") || + strstr (glExtensions, "GL_ARB_texture_rectangle")) + s->textureRectangle = 1; + + if (strstr (glExtensions, "GL_ARB_texture_non_power_of_two")) + s->textureNonPowerOfTwo = 1; + + if (!(s->textureRectangle || s->textureNonPowerOfTwo)) + { + fprintf (stderr, "%s: Support for non power of two textures missing\n", + programName); + return FALSE; + } + + initTexture (s, &s->backgroundTexture); + + s->desktopWindowCount = 0; + + glGetIntegerv (GL_STENCIL_BITS, &stencilBits); + if (!stencilBits) + { + fprintf (stderr, "%s: No stencil buffer. Clipping of transformed " + "windows is not going to be correct when screen is " + "transformed.\n", programName); + } + + glClearColor (0.0, 0.0, 0.0, 1.0); + glBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + glEnable (GL_CULL_FACE); + glDisable (GL_BLEND); + glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glEnableClientState (GL_VERTEX_ARRAY); + glEnableClientState (GL_TEXTURE_COORD_ARRAY); + glColor4usv (defaultColor); + + s->activeWindow = getActiveWindow (display, s->root); + + reshape (s, s->attrib.width, s->attrib.height); + + s->next = display->screens; + display->screens = s; + + screenInitPlugins (s); + + XSelectInput (dpy, s->root, + SubstructureNotifyMask | + StructureNotifyMask | + PropertyChangeMask | + ExposureMask | + ButtonPressMask | + ButtonReleaseMask | + ButtonMotionMask); + + XQueryTree (dpy, s->root, + &rootReturn, &parentReturn, + &children, &nchildren); + + for (i = 0; i < nchildren; i++) + { + if (children[i] == s->grabWindow) + continue; + + addWindow (s, children[i], i ? children[i - 1] : 0); + } + + XFree (children); + + return TRUE; +} + +void +damageScreenRegion (CompScreen *screen, + Region region) +{ + if (screen->allDamaged) + return; + + XUnionRegion (screen->damage, region, screen->damage); +} + +void +damageScreen (CompScreen *s) +{ + s->allDamaged = TRUE; +} + +CompWindow * +findWindowAtScreen (CompScreen *s, + Window id) +{ + CompWindow *w; + + for (w = s->windows; w; w = w->next) + if (w->id == id) + return w; + + return 0; +} + +CompWindow * +findClientWindowAtScreen (CompScreen *s, + Window id) +{ + CompWindow *w; + + for (w = s->windows; w; w = w->next) + if (w->client == id) + return w; + + return 0; +} + +void +insertWindowIntoScreen (CompScreen *s, + CompWindow *w, + Window aboveId) +{ + CompWindow *p; + + if (s->windows) + { + for (p = s->windows; p; p = p->next) + { + if (p->id == aboveId) + { + w->next = p->next; + w->prev = p; + if (p->next) + p->next->prev = w; + p->next = w; + + if (s->reverseWindows == p) + s->reverseWindows = w; + + return; + } + + if (!p->next) + { + p->next = w; + w->next = NULL; + w->prev = p; + + s->reverseWindows = w; + + return; + } + } + } + else + { + s->reverseWindows = s->windows = w; + w->prev = w->next = NULL; + } +} + +void +unhookWindowFromScreen (CompScreen *s, + CompWindow *w) +{ + CompWindow *p; + + if (s->windows == w) + { + s->windows = w->next; + if (w->next) + w->next->prev = NULL; + } + + if (s->reverseWindows == w) + { + s->reverseWindows = w->prev; + if (w->prev) + w->prev->next = NULL; + } + + for (p = s->windows; p; p = p->next) + { + if (p->next == w) + { + p->next = w->next; + if (w->next) + w->next->prev = p; + + p->next = w->next; + break; + } + } +} + +#define POINTER_GRAB_MASK (ButtonReleaseMask | \ + ButtonPressMask | \ + PointerMotionMask) +int +pushScreenGrab (CompScreen *s, + Cursor cursor) +{ + if (s->maxGrab == 0) + { + int status; + + status = XGrabPointer (s->display->display, s->grabWindow, TRUE, + POINTER_GRAB_MASK, + GrabModeAsync, GrabModeAsync, + s->root, cursor, + CurrentTime); + + if (status == GrabSuccess) + { + status = XGrabKeyboard (s->display->display, + s->grabWindow, TRUE, + GrabModeAsync, GrabModeAsync, + CurrentTime); + if (status != GrabSuccess) + { + XUngrabPointer (s->display->display, CurrentTime); + return 0; + } + } + else + return 0; + } + else + { + XChangeActivePointerGrab (s->display->display, POINTER_GRAB_MASK, + cursor, CurrentTime); + } + + if (s->grabSize <= s->maxGrab) + { + s->grabs = realloc (s->grabs, sizeof (CompGrab) * (s->maxGrab + 1)); + if (!s->grabs) + return 0; + + s->grabSize = s->maxGrab + 1; + } + + s->grabs[s->maxGrab].cursor = cursor; + s->grabs[s->maxGrab].active = TRUE; + + s->maxGrab++; + + return s->maxGrab; +} + +void +removeScreenGrab (CompScreen *s, + int index, + XPoint *restorePointer) +{ + int maxGrab; + + index--; + if (index < 0 || index >= s->maxGrab) + abort (); + + s->grabs[index].cursor = None; + s->grabs[index].active = FALSE; + + for (maxGrab = s->maxGrab; maxGrab; maxGrab--) + if (s->grabs[maxGrab - 1].active) + break; + + if (maxGrab != s->maxGrab) + { + if (maxGrab) + { + XChangeActivePointerGrab (s->display->display, + POINTER_GRAB_MASK, + s->grabs[s->maxGrab - 1].cursor, + CurrentTime); + } + else + { + if (restorePointer) + XWarpPointer (s->display->display, None, s->root, 0, 0, 0, 0, + restorePointer->x, restorePointer->y); + + XUngrabPointer (s->display->display, CurrentTime); + XUngrabKeyboard (s->display->display, CurrentTime); + } + s->maxGrab = maxGrab; + } +} + +static Bool +addPassiveKeyGrab (CompScreen *s, + CompKeyBinding *key) +{ + CompKeyGrab *keyGrab; + unsigned int modifiers, mask; + int i; + + modifiers = key->modifiers & ~(CompPressMask | CompReleaseMask); + if (modifiers == key->modifiers) + return TRUE; + + for (i = 0; i < s->nKeyGrab; i++) + { + if (key->keycode == s->keyGrab[i].keycode && + modifiers == s->keyGrab[i].modifiers) + { + s->keyGrab[i].count++; + return TRUE; + } + } + + keyGrab = realloc (s->keyGrab, sizeof (CompKeyGrab) * (s->nKeyGrab + 1)); + if (!keyGrab) + return FALSE; + + s->keyGrab = keyGrab; + + mask = virtualToRealModMask (s->display, modifiers); + if (!(mask & CompNoMask)) + { + compCheckForError (); + + XGrabKey (s->display->display, + key->keycode, + mask, + s->root, + TRUE, + GrabModeAsync, + GrabModeAsync); + + XSync (s->display->display, FALSE); + + if (compCheckForError ()) + { + +#ifdef DEBUG + KeySym keysym; + char *keyname; + + keysym = XKeycodeToKeysym (s->display->display, + key->keycode, + 0); + keyname = XKeysymToString (keysym); + + fprintf (stderr, "XGrabKey failed: %s 0x%x\n", + keyname, modifiers); +#endif + + return FALSE; + } + } + + s->keyGrab[s->nKeyGrab].keycode = key->keycode; + s->keyGrab[s->nKeyGrab].modifiers = modifiers; + s->keyGrab[s->nKeyGrab].count = 1; + + s->nKeyGrab++; + + return TRUE; +} + +static void +removePassiveKeyGrab (CompScreen *s, + CompKeyBinding *key) +{ + unsigned int modifiers, mask; + int i; + + modifiers = key->modifiers & ~(CompPressMask | CompReleaseMask); + if (modifiers == key->modifiers) + return; + + for (i = 0; i < s->nKeyGrab; i++) + { + if (key->keycode == s->keyGrab[i].keycode && + modifiers == s->keyGrab[i].modifiers) + { + s->keyGrab[i].count--; + if (s->keyGrab[i].count) + return; + + s->nKeyGrab--; + s->keyGrab = realloc (s->keyGrab, + sizeof (CompKeyGrab) * s->nKeyGrab); + + mask = virtualToRealModMask (s->display, modifiers); + if (!(mask & CompNoMask)) + { + XUngrabKey (s->display->display, + key->keycode, + mask, + s->root); + } + } + } +} + +static void +updatePassiveKeyGrabs (CompScreen *s) +{ + unsigned int mask; + int i; + + XUngrabKey (s->display->display, AnyKey, AnyModifier, s->root); + + for (i = 0; i < s->nKeyGrab; i++) + { + mask = virtualToRealModMask (s->display, s->keyGrab[i].modifiers); + if (!(mask & CompNoMask)) + { + XGrabKey (s->display->display, + s->keyGrab[i].keycode, + mask, + s->root, + TRUE, + GrabModeAsync, + GrabModeAsync); + } + } +} + +static Bool +addPassiveButtonGrab (CompScreen *s, + CompButtonBinding *button) +{ + CompButtonGrab *buttonGrab; + unsigned int modifiers, mask; + int i; + + modifiers = button->modifiers & ~(CompPressMask | CompReleaseMask); + if (modifiers == button->modifiers) + return TRUE; + + for (i = 0; i < s->nButtonGrab; i++) + { + if (button->button == s->buttonGrab[i].button && + modifiers == s->buttonGrab[i].modifiers) + { + s->buttonGrab[i].count++; + return TRUE; + } + } + + buttonGrab = realloc (s->buttonGrab, + sizeof (CompButtonGrab) * (s->nButtonGrab + 1)); + if (!buttonGrab) + return FALSE; + + s->buttonGrab = buttonGrab; + + mask = virtualToRealModMask (s->display, modifiers); + if (!(mask & CompNoMask)) + { + compCheckForError (); + + XGrabButton (s->display->display, + button->button, + mask, + s->root, + TRUE, + POINTER_GRAB_MASK, + GrabModeAsync, + GrabModeAsync, + None, + None); + + XSync (s->display->display, FALSE); + + if (compCheckForError ()) + { + +#ifdef DEBUG + fprintf (stderr, "XGrabButton failed: %s 0x%x\n", + button->button, modifiers); +#endif + + return FALSE; + } + } + + s->buttonGrab[s->nButtonGrab].button = button->button; + s->buttonGrab[s->nButtonGrab].modifiers = modifiers; + s->buttonGrab[s->nButtonGrab].count = 1; + + s->nButtonGrab++; + + return TRUE; +} + +static void +removePassiveButtonGrab (CompScreen *s, + CompButtonBinding *button) +{ + unsigned int modifiers, mask; + int i; + + modifiers = button->modifiers & ~(CompPressMask | CompReleaseMask); + if (modifiers == button->modifiers) + return; + + for (i = 0; i < s->nButtonGrab; i++) + { + if (button->button == s->buttonGrab[i].button && + modifiers == s->buttonGrab[i].modifiers) + { + s->buttonGrab[i].count--; + if (s->buttonGrab[i].count) + return; + + s->nButtonGrab--; + s->buttonGrab = realloc (s->buttonGrab, + sizeof (CompButtonGrab) * s->nButtonGrab); + + mask = virtualToRealModMask (s->display, modifiers); + if (!(mask & CompNoMask)) + { + XUngrabButton (s->display->display, + button->button, + mask, + s->root); + } + } + } +} + +static void +updatePassiveButtonGrabs (CompScreen *s) +{ + unsigned int mask; + int i; + + XUngrabButton (s->display->display, AnyButton, AnyModifier, s->root); + + for (i = 0; i < s->nButtonGrab; i++) + { + mask = virtualToRealModMask (s->display, s->buttonGrab[i].modifiers); + if (!(mask & CompNoMask)) + { + XGrabButton (s->display->display, + s->buttonGrab[i].button, + mask, + s->root, + TRUE, + POINTER_GRAB_MASK, + GrabModeAsync, + GrabModeAsync, + None, + None); + } + } +} + +Bool +addScreenBinding (CompScreen *s, + CompBinding *binding) +{ + if (binding->type == CompBindingTypeKey) + return addPassiveKeyGrab (s, &binding->u.key); + else if (binding->type == CompBindingTypeButton) + return addPassiveButtonGrab (s, &binding->u.button); + + return FALSE; +} + +void +removeScreenBinding (CompScreen *s, + CompBinding *binding) +{ + if (binding->type == CompBindingTypeKey) + removePassiveKeyGrab (s, &binding->u.key); + else if (binding->type == CompBindingTypeButton) + removePassiveButtonGrab (s, &binding->u.button); +} + +void +updatePassiveGrabs (CompScreen *s) +{ + updatePassiveButtonGrabs (s); + updatePassiveKeyGrabs (s); +} diff --git a/src/texture.c b/src/texture.c new file mode 100644 index 0000000..6ef4e74 --- /dev/null +++ b/src/texture.c @@ -0,0 +1,286 @@ +/* + * Copyright © 2005 Novell, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without + * fee, provided that the above copyright notice appear in all copies + * and that both that copyright notice and this permission notice + * appear in supporting documentation, and that the name of + * Novell, Inc. not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior permission. + * Novell, Inc. makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + * + * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN + * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Author: David Reveman <davidr@novell.com> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <comp.h> + +void +initTexture (CompScreen *screen, + CompTexture *texture) +{ + texture->name = 0; + texture->target = GL_TEXTURE_2D; + texture->dx = 0.0f; + texture->dy = 0.0f; + texture->pixmap = None; + texture->filter = COMP_TEXTURE_FILTER_FAST; +} + +void +finiTexture (CompScreen *screen, + CompTexture *texture) +{ + if (texture->name) + { + releasePixmapFromTexture (screen, texture); + glDeleteTextures (1, &texture->name); + } +} + +Bool +readImageToTexture (CompScreen *screen, + CompTexture *texture, + char *imageFileName, + unsigned int *returnWidth, + unsigned int *returnHeight) +{ + char *data, *image; + unsigned int width, height; + int i; + + if (!readPng (imageFileName, &image, &width, &height)) + { + fprintf (stderr, "%s: Failed to load image: %s\n", + programName, imageFileName); + return FALSE; + } + + data = malloc (4 * width * height); + if (!data) + { + free (image); + return FALSE; + } + + for (i = 0; i < height; i++) + memcpy (&data[i * width * 4], + &image[(height - i - 1) * width * 4], + width * 4); + + free (image); + + releasePixmapFromTexture (screen, texture); + + if (screen->textureNonPowerOfTwo || + (POWER_OF_TWO (width) && POWER_OF_TWO (height))) + { + texture->target = GL_TEXTURE_2D; + texture->dx = 1.0f / width; + texture->dy = 1.0f / height; + } + else + { + texture->target = GL_TEXTURE_RECTANGLE_NV; + texture->dx = texture->dy = 1.0f; + } + + if (!texture->name) + glGenTextures (1, &texture->name); + + glBindTexture (texture->target, texture->name); + + glTexImage2D (texture->target, 0, GL_RGB, width, height, 0, GL_BGRA, + +#if IMAGE_BYTE_ORDER == MSBFirst + GL_UNSIGNED_INT_8_8_8_8_REV, +#else + GL_UNSIGNED_BYTE, +#endif + + data); + + texture->filter = COMP_TEXTURE_FILTER_FAST; + + glTexParameteri (texture->target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri (texture->target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + glTexParameteri (texture->target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri (texture->target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glBindTexture (texture->target, 0); + + free (data); + + *returnWidth = width; + *returnHeight = height; + + return TRUE; +} + +Bool +bindPixmapToTexture (CompScreen *screen, + CompTexture *texture, + Pixmap pixmap, + int width, + int height, + int depth) +{ + XVisualInfo *visinfo; + unsigned int target; + + visinfo = screen->glxPixmapVisuals[depth]; + if (!visinfo) + { + fprintf (stderr, "%s: No GL visual for depth %d\n", + programName, depth); + + return FALSE; + } + + texture->pixmap = glXCreateGLXPixmap (screen->display->display, + visinfo, pixmap); + if (!texture->pixmap) + { + fprintf (stderr, "%s: glXCreateGLXPixmap failed\n", programName); + + return FALSE; + } + + if (screen->queryDrawable (screen->display->display, + texture->pixmap, + GLX_TEXTURE_TARGET_EXT, + &target)) + { + fprintf (stderr, "%s: glXQueryDrawable failed\n", programName); + + glXDestroyGLXPixmap (screen->display->display, texture->pixmap); + texture->pixmap = None; + + return FALSE; + } + + switch (target) { + case GLX_TEXTURE_2D_EXT: + texture->target = GL_TEXTURE_2D; + texture->dx = 1.0f / width; + texture->dy = 1.0f / height; + break; + case GLX_TEXTURE_RECTANGLE_EXT: + texture->target = GL_TEXTURE_RECTANGLE_ARB; + texture->dx = texture->dy = 1.0f; + break; + default: + fprintf (stderr, "%s: pixmap 0x%x can't be bound to texture\n", + programName, (int) pixmap); + + glXDestroyGLXPixmap (screen->display->display, texture->pixmap); + texture->pixmap = None; + + return FALSE; + } + + if (!texture->name) + glGenTextures (1, &texture->name); + + glBindTexture (texture->target, texture->name); + + if (!screen->bindTexImage (screen->display->display, + texture->pixmap, + GLX_FRONT_LEFT_EXT)) + { + fprintf (stderr, "%s: glXBindTexImage failed\n", programName); + + glXDestroyGLXPixmap (screen->display->display, texture->pixmap); + texture->pixmap = None; + + return FALSE; + } + + texture->filter = COMP_TEXTURE_FILTER_FAST; + + glTexParameteri (texture->target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri (texture->target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + glTexParameteri (texture->target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri (texture->target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glBindTexture (texture->target, 0); + + return TRUE; +} + +void +releasePixmapFromTexture (CompScreen *screen, + CompTexture *texture) +{ + if (texture->pixmap) + { + glEnable (texture->target); + glBindTexture (texture->target, texture->name); + + screen->releaseTexImage (screen->display->display, + texture->pixmap, + GLX_FRONT_LEFT_EXT); + + glBindTexture (texture->target, 0); + glDisable (texture->target); + + glXDestroyGLXPixmap (screen->display->display, texture->pixmap); + texture->pixmap = None; + } +} + +void +enableTexture (CompScreen *screen, + CompTexture *texture, + CompTextureFilter filter) +{ + glEnable (texture->target); + glBindTexture (texture->target, texture->name); + + if (filter != texture->filter) + { + switch (filter) { + case COMP_TEXTURE_FILTER_FAST: + glTexParameteri (texture->target, + GL_TEXTURE_MIN_FILTER, + GL_NEAREST); + glTexParameteri (texture->target, + GL_TEXTURE_MAG_FILTER, + GL_NEAREST); + break; + case COMP_TEXTURE_FILTER_GOOD: + glTexParameteri (texture->target, + GL_TEXTURE_MIN_FILTER, + screen->display->textureFilter); + glTexParameteri (texture->target, + GL_TEXTURE_MAG_FILTER, + screen->display->textureFilter); + break; + } + + texture->filter = filter; + } +} + +void +disableTexture (CompTexture *texture) +{ + glBindTexture (texture->target, 0); + glDisable (texture->target); +} diff --git a/src/window.c b/src/window.c new file mode 100644 index 0000000..8c9a126 --- /dev/null +++ b/src/window.c @@ -0,0 +1,605 @@ +/* + * Copyright © 2005 Novell, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without + * fee, provided that the above copyright notice appear in all copies + * and that both that copyright notice and this permission notice + * appear in supporting documentation, and that the name of + * Novell, Inc. not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior permission. + * Novell, Inc. makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + * + * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN + * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Author: David Reveman <davidr@novell.com> + */ + +#include <X11/Xlib.h> +#include <X11/Xatom.h> +#include <X11/extensions/shape.h> +#include <X11/extensions/Xcomposite.h> + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include <comp.h> + +static int +reallocWindowPrivates (int size, + void *closure) +{ + CompScreen *s = (CompScreen *) closure; + CompWindow *w; + void *privates; + + for (w = s->windows; w; w = w->next) + { + privates = realloc (w->privates, size * sizeof (CompPrivate)); + if (!privates) + return FALSE; + + w->privates = (CompPrivate *) privates; + } + + return TRUE; +} + +int +allocateWindowPrivateIndex (CompScreen *screen) +{ + return allocatePrivateIndex (&screen->windowPrivateLen, + &screen->windowPrivateIndices, + reallocWindowPrivates, + (void *) screen); +} + +void +freeWindowPrivateIndex (CompScreen *screen, + int index) +{ + freePrivateIndex (screen->windowPrivateLen, + screen->windowPrivateIndices, + index); +} + +static Window +tryChildren (CompDisplay *display, + Window win) +{ + Window root, parent; + Window *children; + unsigned int nchildren; + unsigned int i; + Atom type = None; + int format; + unsigned long nitems, after; + unsigned char *data; + Window inf = 0; + + if (!XQueryTree (display->display, win, &root, &parent, + &children, &nchildren)) + return 0; + + for (i = 0; !inf && (i < nchildren); i++) + { + data = NULL; + + XGetWindowProperty (display->display, children[i], + display->wmStateAtom, 0, 0, + False, AnyPropertyType, &type, &format, &nitems, + &after, &data); + if (data) + XFree (data); + + if (type) + inf = children[i]; + } + + for (i = 0; !inf && (i < nchildren); i++) + inf = tryChildren (display, children[i]); + + if (children) + XFree (children); + + return inf; +} + +static Window +clientWindow (CompDisplay *display, + Window win) +{ + Atom type = None; + int format; + unsigned long nitems, after; + unsigned char *data = NULL; + Window inf; + + XGetWindowProperty (display->display, win, display->wmStateAtom, 0, 0, + False, AnyPropertyType, &type, &format, &nitems, + &after, &data); + + if (data) + XFree (data); + + if (type) + return win; + + inf = tryChildren (display, win); + if (!inf) + inf = win; + + return inf; +} + +Window +getActiveWindow (CompDisplay *display, + Window root) +{ + Atom actual; + int result, format; + unsigned long n, left; + unsigned char *data; + + result = XGetWindowProperty (display->display, root, + display->winActiveAtom, 0L, 1L, False, + XA_WINDOW, &actual, &format, + &n, &left, &data); + + if (result == Success && n && data) + { + Window w; + + memcpy (&w, data, sizeof (Window)); + XFree ((void *) data); + + return w; + } + + return None; +} + +Atom +getWindowType (CompDisplay *display, + Window id) +{ + Atom actual; + int result, format; + unsigned long n, left; + unsigned char *data; + + result = XGetWindowProperty (display->display, id, display->winTypeAtom, + 0L, 1L, FALSE, XA_ATOM, &actual, &format, + &n, &left, &data); + + if (result == Success && n && data) + { + Atom a; + + memcpy (&a, data, sizeof (Atom)); + XFree ((void *) data); + + return a; + } + + return display->winNormalAtom; +} + +unsigned short +getWindowOpacity (CompDisplay *display, + Window id) +{ + Atom actual; + int result, format; + unsigned long n, left; + unsigned char *data; + + result = XGetWindowProperty (display->display, id, display->winOpacityAtom, + 0L, 1L, FALSE, XA_CARDINAL, &actual, &format, + &n, &left, &data); + + if (result == Success && n && data) + { + unsigned int o; + + memcpy (&o, data, sizeof (Atom)); + XFree ((void *) data); + + return (o / 0xffff); + } + + return MAXSHORT; +} + +void +bindWindow (CompWindow *w) +{ + if (testMode) + { + unsigned int width, height; + + if (readImageToTexture (w->screen, &w->texture, + windowImage, &width, &height)) + { + XResizeWindow (w->screen->display->display, w->id, width, height); + + w->width = width; + w->height = height; + } + + w->pixmap = 1; + } + else + { + w->pixmap = XCompositeNameWindowPixmap (w->screen->display->display, + w->id); + if (!w->pixmap) + { + fprintf (stderr, "%s: XCompositeNameWindowPixmap failed\n", + programName); + return; + } + + if (!bindPixmapToTexture (w->screen, &w->texture, w->pixmap, + w->width, w->height, + w->attrib.depth)) + { + fprintf (stderr, "%s: Couldn't bind redirected window 0x%x to " + "texture\n", programName, (int) w->id); + } + } +} + +void +releaseWindow (CompWindow *w) +{ + if (w->pixmap) + { + releasePixmapFromTexture (w->screen, &w->texture); + + if (!testMode) + XFreePixmap (w->screen->display->display, w->pixmap); + + w->pixmap = None; + } +} + +static void +freeWindow (CompWindow *w) +{ + releaseWindow (w); + + if (w->texture.name) + finiTexture (w->screen, &w->texture); + + if (w->clip) + XDestroyRegion (w->clip); + + if (w->region) + XDestroyRegion (w->region); + + if (w->privates) + free (w->privates); + + free (w); +} + +void +addWindowDamage (CompWindow *w) +{ + if (w->attrib.map_state == IsViewable) + damageScreenRegion (w->screen, w->region); +} + +void +updateWindowRegion (CompWindow *w) +{ + REGION rect; + XRectangle r, *rects, *shapeRects = 0; + int i, n = 0; + + EMPTY_REGION (w->region); + + if (w->screen->display->shapeExtension) + { + int order; + + shapeRects = XShapeGetRectangles (w->screen->display->display, w->id, + ShapeBounding, &n, &order); + } + + if (n < 2) + { + r.x = 0; + r.y = 0; + r.width = w->width; + r.height = w->height; + + rects = &r; + n = 1; + } + else + { + rects = shapeRects; + } + + rect.rects = &rect.extents; + rect.numRects = rect.size = 1; + + for (i = 0; i < n; i++) + { + rect.extents.x1 = rects[i].x + w->attrib.x; + rect.extents.y1 = rects[i].y + w->attrib.y; + rect.extents.x2 = rect.extents.x1 + rects[i].width; + rect.extents.y2 = rect.extents.y1 + rects[i].height; + + XUnionRegion (&rect, w->region, w->region); + } + + if (shapeRects) + XFree (shapeRects); +} + +void +addWindow (CompScreen *screen, + Window id, + Window aboveId) +{ + CompWindow *w; + + w = (CompWindow *) malloc (sizeof (CompWindow)); + if (!w) + return; + + w->next = NULL; + w->prev = NULL; + + w->screen = screen; + w->texture.name = 0; + w->pixmap = None; + w->destroyed = FALSE; + + if (screen->windowPrivateLen) + { + w->privates = malloc (screen->windowPrivateLen * sizeof (CompPrivate)); + if (!w->privates) + { + free (w); + return; + } + } + else + w->privates = 0; + + w->region = XCreateRegion (); + if (!w->region) + { + freeWindow (w); + return; + } + + w->clip = XCreateRegion (); + if (!w->clip) + { + freeWindow (w); + return; + } + + if (!XGetWindowAttributes (screen->display->display, id, &w->attrib)) + { + freeWindow (w); + return; + } + + XSelectInput (screen->display->display, id, PropertyChangeMask); + + w->id = id; + w->client = clientWindow (screen->display, id); + w->alpha = (w->attrib.depth == 32); + w->opacity = OPAQUE; + w->type = getWindowType (screen->display, w->client); + + w->width = w->attrib.width + w->attrib.border_width * 2; + w->height = w->attrib.height + w->attrib.border_width * 2; + + if (screen->display->shapeExtension) + XShapeSelectInput (screen->display->display, id, ShapeNotifyMask); + + updateWindowRegion (w); + + insertWindowIntoScreen (screen, w, aboveId); + + if (w->attrib.class != InputOnly) + { + initTexture (screen, &w->texture); + + w->damage = XDamageCreate (screen->display->display, id, + XDamageReportRawRectangles); + } + else + { + w->damage = None; + w->attrib.map_state = IsUnmapped; + } + + if (testMode) + { + w->attrib.map_state = IsViewable; + bindWindow (w); + } + + w->invisible = WINDOW_INVISIBLE (w); + + if (w->type == w->screen->display->winDesktopAtom) + w->screen->desktopWindowCount++; + + windowInitPlugins (w); + + addWindowDamage (w); +} + +void +removeWindow (CompWindow *w) +{ + if (w->attrib.map_state == IsViewable) + { + if (w->type == w->screen->display->winDesktopAtom) + w->screen->desktopWindowCount++; + } + + unhookWindowFromScreen (w->screen, w); + windowFiniPlugins (w); + freeWindow (w); +} + +void +destroyWindow (CompWindow *w) +{ + if (!w->destroyed) + { + w->destroyed = TRUE; + w->screen->pendingDestroys++; + } +} + +void +mapWindow (CompWindow *w) +{ + if (w->attrib.class == InputOnly) + return; + + if (w->attrib.map_state == IsViewable) + return; + + if (w->type == w->screen->display->winDesktopAtom) + w->screen->desktopWindowCount++; + + w->attrib.map_state = IsViewable; + w->invisible = WINDOW_INVISIBLE (w); + + addWindowDamage (w); +} + +void +unmapWindow (CompWindow *w) +{ + if (w->attrib.map_state != IsViewable) + return; + + if (w->type == w->screen->display->winDesktopAtom) + w->screen->desktopWindowCount--; + + addWindowDamage (w); + + w->attrib.map_state = IsUnmapped; + w->invisible = WINDOW_INVISIBLE (w); + + releaseWindow (w); +} + +static int +restackWindow (CompWindow *w, + Window aboveId) +{ + if (w->prev) + { + if (aboveId == w->prev->id) + return 0; + } + else if (aboveId == None) + return 0; + + unhookWindowFromScreen (w->screen, w); + insertWindowIntoScreen (w->screen, w, aboveId); + + return 1; +} + +void +configureWindow (CompWindow *w, + XConfigureEvent *ce) +{ + Bool damage; + + if (w->attrib.width != ce->width || + w->attrib.height != ce->height || + w->attrib.border_width != ce->border_width) + { + addWindowDamage (w); + + w->attrib.x = ce->x; + w->attrib.y = ce->y; + w->attrib.width = ce->width; + w->attrib.height = ce->height; + w->attrib.border_width = ce->border_width; + + w->width = w->attrib.width + w->attrib.border_width * 2; + w->height = w->attrib.height + w->attrib.border_width * 2; + + releaseWindow (w); + + EMPTY_REGION (w->region); + + damage = TRUE; + } + else if (w->attrib.x != ce->x || w->attrib.y != ce->y) + { + addWindowDamage (w); + + XOffsetRegion (w->region, ce->x - w->attrib.x, ce->y - w->attrib.y); + + w->attrib.x = ce->x; + w->attrib.y = ce->y; + + damage = TRUE; + } + else + damage = FALSE; + + w->attrib.override_redirect = ce->override_redirect; + + w->invisible = WINDOW_INVISIBLE (w); + + if (restackWindow (w, ce->above) || damage) + { + if (!REGION_NOT_EMPTY (w->region)) + updateWindowRegion (w); + + addWindowDamage (w); + } +} + +void +circulateWindow (CompWindow *w, + XCirculateEvent *ce) +{ + Window newAboveId; + + if (ce->place == PlaceOnTop && w->screen->windows) + newAboveId = w->screen->windows->id; + else + newAboveId = 0; + + if (restackWindow (w, newAboveId)) + addWindowDamage (w); +} + +void +invisibleWindowMove (CompWindow *w, + int dx, + int dy) +{ + w->attrib.x += dx; + w->attrib.y += dy; + + XOffsetRegion (w->region, dx, dy); +} |