diff options
Diffstat (limited to 'src/display.c')
-rw-r--r-- | src/display.c | 2809 |
1 files changed, 2809 insertions, 0 deletions
diff --git a/src/display.c b/src/display.c new file mode 100644 index 0000000..caf63d4 --- /dev/null +++ b/src/display.c @@ -0,0 +1,2809 @@ +/* + * Copyright © 2005 Novell, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without + * fee, provided that the above copyright notice appear in all copies + * and that both that copyright notice and this permission notice + * appear in supporting documentation, and that the name of + * Novell, Inc. not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior permission. + * Novell, Inc. makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + * + * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN + * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Author: David Reveman <davidr@novell.com> + */ + +#ifdef HAVE_CONFIG_H +# include "../config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/poll.h> +#include <assert.h> + +#define XK_MISCELLANY +#include <X11/keysymdef.h> + +#include <X11/Xlib.h> +#include <X11/Xatom.h> +#include <X11/extensions/Xcomposite.h> +#include <X11/extensions/Xrandr.h> +#include <X11/extensions/shape.h> + +#include <compiz-core.h> + +static unsigned int virtualModMask[] = { + CompAltMask, CompMetaMask, CompSuperMask, CompHyperMask, + CompModeSwitchMask, CompNumLockMask, CompScrollLockMask +}; + +static CompScreen *targetScreen = NULL; +static CompOutput *targetOutput; + +static Bool inHandleEvent = FALSE; + +static const CompTransform identity = { + { + 1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0 + } +}; + +int lastPointerX = 0; +int lastPointerY = 0; +int pointerX = 0; +int pointerY = 0; + +#define NUM_OPTIONS(d) (sizeof ((d)->opt) / sizeof (CompOption)) + +static char *displayPrivateIndices = 0; +static int displayPrivateLen = 0; + +static int +reallocDisplayPrivate (int size, + void *closure) +{ + CompDisplay *d; + void *privates; + + for (d = core.displays; d; d = d->next) + { + privates = realloc (d->base.privates, size * sizeof (CompPrivate)); + if (!privates) + return FALSE; + + d->base.privates = (CompPrivate *) privates; + } + + return TRUE; +} + +int +allocDisplayObjectPrivateIndex (CompObject *parent) +{ + return allocatePrivateIndex (&displayPrivateLen, + &displayPrivateIndices, + reallocDisplayPrivate, + 0); +} + +void +freeDisplayObjectPrivateIndex (CompObject *parent, + int index) +{ + freePrivateIndex (displayPrivateLen, displayPrivateIndices, index); +} + +CompBool +forEachDisplayObject (CompObject *parent, + ObjectCallBackProc proc, + void *closure) +{ + if (parent->type == COMP_OBJECT_TYPE_CORE) + { + CompDisplay *d; + + for (d = core.displays; d; d = d->next) + { + if (!(*proc) (&d->base, closure)) + return FALSE; + } + } + + return TRUE; +} + +char * +nameDisplayObject (CompObject *object) +{ + return NULL; +} + +CompObject * +findDisplayObject (CompObject *parent, + const char *name) +{ + if (parent->type == COMP_OBJECT_TYPE_CORE) + { + if (!name || !name[0]) + return &core.displays->base; + } + + return NULL; +} + +int +allocateDisplayPrivateIndex (void) +{ + return compObjectAllocatePrivateIndex (NULL, COMP_OBJECT_TYPE_DISPLAY); +} + +void +freeDisplayPrivateIndex (int index) +{ + compObjectFreePrivateIndex (NULL, COMP_OBJECT_TYPE_DISPLAY, index); +} + +static Bool +closeWin (CompDisplay *d, + CompAction *action, + CompActionState state, + CompOption *option, + int nOption) +{ + CompWindow *w; + Window xid; + unsigned int time; + + xid = getIntOptionNamed (option, nOption, "window", 0); + time = getIntOptionNamed (option, nOption, "time", CurrentTime); + + w = findTopLevelWindowAtDisplay (d, xid); + if (w && (w->actions & CompWindowActionCloseMask)) + closeWindow (w, time); + + return TRUE; +} + +static Bool +unmaximize (CompDisplay *d, + CompAction *action, + CompActionState state, + CompOption *option, + int nOption) +{ + CompWindow *w; + Window xid; + + xid = getIntOptionNamed (option, nOption, "window", 0); + + w = findTopLevelWindowAtDisplay (d, xid); + if (w) + maximizeWindow (w, 0); + + return TRUE; +} + +static Bool +minimize (CompDisplay *d, + CompAction *action, + CompActionState state, + CompOption *option, + int nOption) +{ + CompWindow *w; + Window xid; + + xid = getIntOptionNamed (option, nOption, "window", 0); + + w = findTopLevelWindowAtDisplay (d, xid); + if (w && (w->actions & CompWindowActionMinimizeMask)) + minimizeWindow (w); + + return TRUE; +} + +static Bool +maximize (CompDisplay *d, + CompAction *action, + CompActionState state, + CompOption *option, + int nOption) +{ + CompWindow *w; + Window xid; + + xid = getIntOptionNamed (option, nOption, "window", 0); + + w = findTopLevelWindowAtDisplay (d, xid); + if (w) + maximizeWindow (w, MAXIMIZE_STATE); + + return TRUE; +} + +static Bool +maximizeHorizontally (CompDisplay *d, + CompAction *action, + CompActionState state, + CompOption *option, + int nOption) +{ + CompWindow *w; + Window xid; + + xid = getIntOptionNamed (option, nOption, "window", 0); + + w = findTopLevelWindowAtDisplay (d, xid); + if (w) + maximizeWindow (w, w->state | CompWindowStateMaximizedHorzMask); + + return TRUE; +} + +static Bool +maximizeVertically (CompDisplay *d, + CompAction *action, + CompActionState state, + CompOption *option, + int nOption) +{ + CompWindow *w; + Window xid; + + xid = getIntOptionNamed (option, nOption, "window", 0); + + w = findTopLevelWindowAtDisplay (d, xid); + if (w) + maximizeWindow (w, w->state | CompWindowStateMaximizedVertMask); + + return TRUE; +} + +static Bool +showDesktop (CompDisplay *d, + CompAction *action, + CompActionState state, + CompOption *option, + int nOption) +{ + CompScreen *s; + Window xid; + + xid = getIntOptionNamed (option, nOption, "root", 0); + + s = findScreenAtDisplay (d, xid); + if (s) + { + if (s->showingDesktopMask == 0) + (*s->enterShowDesktopMode) (s); + else + (*s->leaveShowDesktopMode) (s, NULL); + } + + return TRUE; +} + +static Bool +toggleSlowAnimations (CompDisplay *d, + CompAction *action, + CompActionState state, + CompOption *option, + int nOption) +{ + CompScreen *s; + Window xid; + + xid = getIntOptionNamed (option, nOption, "root", 0); + + s = findScreenAtDisplay (d, xid); + if (s) + s->slowAnimations = !s->slowAnimations; + + return TRUE; +} + +static Bool +raiseInitiate (CompDisplay *d, + CompAction *action, + CompActionState state, + CompOption *option, + int nOption) +{ + CompWindow *w; + Window xid; + + xid = getIntOptionNamed (option, nOption, "window", 0); + + w = findTopLevelWindowAtDisplay (d, xid); + if (w) + raiseWindow (w); + + return TRUE; +} + +static Bool +lowerInitiate (CompDisplay *d, + CompAction *action, + CompActionState state, + CompOption *option, + int nOption) +{ + CompWindow *w; + Window xid; + + xid = getIntOptionNamed (option, nOption, "window", 0); + + w = findTopLevelWindowAtDisplay (d, xid); + if (w) + lowerWindow (w); + + return TRUE; +} + +static Bool +windowMenu (CompDisplay *d, + CompAction *action, + CompActionState state, + CompOption *option, + int nOption) +{ + CompWindow *w; + Window xid; + + xid = getIntOptionNamed (option, nOption, "window", 0); + + w = findTopLevelWindowAtDisplay (d, xid); + if (w && !w->screen->maxGrab) + { + int x, y, button; + Time time; + + time = getIntOptionNamed (option, nOption, "time", CurrentTime); + button = getIntOptionNamed (option, nOption, "button", 0); + x = getIntOptionNamed (option, nOption, "x", w->attrib.x); + y = getIntOptionNamed (option, nOption, "y", w->attrib.y); + + toolkitAction (w->screen, + w->screen->display->toolkitActionWindowMenuAtom, + time, + w->id, + button, + x, + y); + } + + return TRUE; +} + +static Bool +toggleMaximized (CompDisplay *d, + CompAction *action, + CompActionState state, + CompOption *option, + int nOption) +{ + CompWindow *w; + Window xid; + + xid = getIntOptionNamed (option, nOption, "window", 0); + + w = findTopLevelWindowAtDisplay (d, xid); + if (w) + { + if ((w->state & MAXIMIZE_STATE) == MAXIMIZE_STATE) + maximizeWindow (w, 0); + else + maximizeWindow (w, MAXIMIZE_STATE); + } + + return TRUE; +} + +static Bool +toggleMaximizedHorizontally (CompDisplay *d, + CompAction *action, + CompActionState state, + CompOption *option, + int nOption) +{ + CompWindow *w; + Window xid; + + xid = getIntOptionNamed (option, nOption, "window", 0); + + w = findTopLevelWindowAtDisplay (d, xid); + if (w) + maximizeWindow (w, w->state ^ CompWindowStateMaximizedHorzMask); + + return TRUE; +} + +static Bool +toggleMaximizedVertically (CompDisplay *d, + CompAction *action, + CompActionState state, + CompOption *option, + int nOption) +{ + CompWindow *w; + Window xid; + + xid = getIntOptionNamed (option, nOption, "window", 0); + + w = findTopLevelWindowAtDisplay (d, xid); + if (w) + maximizeWindow (w, w->state ^ CompWindowStateMaximizedVertMask); + + return TRUE; +} + +static Bool +shade (CompDisplay *d, + CompAction *action, + CompActionState state, + CompOption *option, + int nOption) +{ + CompWindow *w; + Window xid; + + xid = getIntOptionNamed (option, nOption, "window", 0); + + w = findTopLevelWindowAtDisplay (d, xid); + if (w && (w->actions & CompWindowActionShadeMask)) + { + w->state ^= CompWindowStateShadedMask; + updateWindowAttributes (w, CompStackingUpdateModeNone); + } + + return TRUE; +} + +const CompMetadataOptionInfo coreDisplayOptionInfo[COMP_DISPLAY_OPTION_NUM] = { + { "abi", "int", 0, 0, 0 }, + { "active_plugins", "list", "<type>string</type>", 0, 0 }, + { "texture_filter", "int", RESTOSTRING (0, 2), 0, 0 }, + { "click_to_focus", "bool", 0, 0, 0 }, + { "autoraise", "bool", 0, 0, 0 }, + { "autoraise_delay", "int", 0, 0, 0 }, + { "close_window_key", "key", 0, closeWin, 0 }, + { "close_window_button", "button", 0, closeWin, 0 }, + { "slow_animations_key", "key", 0, toggleSlowAnimations, 0 }, + { "raise_window_key", "key", 0, raiseInitiate, 0 }, + { "raise_window_button", "button", 0, raiseInitiate, 0 }, + { "lower_window_key", "key", 0, lowerInitiate, 0 }, + { "lower_window_button", "button", 0, lowerInitiate, 0 }, + { "unmaximize_window_key", "key", 0, unmaximize, 0 }, + { "minimize_window_key", "key", 0, minimize, 0 }, + { "minimize_window_button", "button", 0, minimize, 0 }, + { "maximize_window_key", "key", 0, maximize, 0 }, + { "maximize_window_horizontally_key", "key", 0, maximizeHorizontally, 0 }, + { "maximize_window_vertically_key", "key", 0, maximizeVertically, 0 }, + { "window_menu_button", "button", 0, windowMenu, 0 }, + { "window_menu_key", "key", 0, windowMenu, 0 }, + { "show_desktop_key", "key", 0, showDesktop, 0 }, + { "show_desktop_edge", "edge", 0, showDesktop, 0 }, + { "raise_on_click", "bool", 0, 0, 0 }, + { "audible_bell", "bool", 0, 0, 0 }, + { "toggle_window_maximized_key", "key", 0, toggleMaximized, 0 }, + { "toggle_window_maximized_button", "button", 0, toggleMaximized, 0 }, + { "toggle_window_maximized_horizontally_key", "key", 0, + toggleMaximizedHorizontally, 0 }, + { "toggle_window_maximized_vertically_key", "key", 0, + toggleMaximizedVertically, 0 }, + { "hide_skip_taskbar_windows", "bool", 0, 0, 0 }, + { "toggle_window_shaded_key", "key", 0, shade, 0 }, + { "ignore_hints_when_maximized", "bool", 0, 0, 0 }, + { "ping_delay", "int", "<min>1000</min>", 0, 0 }, + { "edge_delay", "int", "<min>0</min>", 0, 0 } +}; + +CompOption * +getDisplayOptions (CompPlugin *plugin, + CompDisplay *display, + int *count) +{ + *count = NUM_OPTIONS (display); + return display->opt; +} + +static void +setAudibleBell (CompDisplay *display, + Bool audible) +{ + if (display->xkbExtension) + XkbChangeEnabledControls (display->display, + XkbUseCoreKbd, + XkbAudibleBellMask, + audible ? XkbAudibleBellMask : 0); +} + +static Bool +pingTimeout (void *closure) +{ + CompDisplay *d = closure; + CompScreen *s; + CompWindow *w; + XEvent ev; + int ping = d->lastPing + 1; + + ev.type = ClientMessage; + ev.xclient.window = 0; + ev.xclient.message_type = d->wmProtocolsAtom; + ev.xclient.format = 32; + ev.xclient.data.l[0] = d->wmPingAtom; + ev.xclient.data.l[1] = ping; + ev.xclient.data.l[2] = 0; + ev.xclient.data.l[3] = 0; + ev.xclient.data.l[4] = 0; + + for (s = d->screens; s; s = s->next) + { + for (w = s->windows; w; w = w->next) + { + if (w->attrib.map_state != IsViewable) + continue; + + if (!(w->type & CompWindowTypeNormalMask)) + continue; + + if (w->protocols & CompWindowProtocolPingMask) + { + if (w->transientFor) + continue; + + if (w->lastPong < d->lastPing) + { + if (w->alive) + { + w->alive = FALSE; + + if (w->closeRequests) + { + toolkitAction (s, + d->toolkitActionForceQuitDialogAtom, + w->lastCloseRequestTime, + w->id, + TRUE, + 0, + 0); + + w->closeRequests = 0; + } + + addWindowDamage (w); + } + } + + ev.xclient.window = w->id; + ev.xclient.data.l[2] = w->id; + + XSendEvent (d->display, w->id, FALSE, NoEventMask, &ev); + } + } + } + + d->lastPing = ping; + + return TRUE; +} + +Bool +setDisplayOption (CompPlugin *plugin, + CompDisplay *display, + const 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_ABI: + break; + case COMP_DISPLAY_OPTION_ACTIVE_PLUGINS: + if (compSetOptionList (o, value)) + { + display->dirtyPluginList = TRUE; + return TRUE; + } + break; + case COMP_DISPLAY_OPTION_TEXTURE_FILTER: + if (compSetIntOption (o, value)) + { + CompScreen *s; + + for (s = display->screens; s; s = s->next) + damageScreen (s); + + if (!o->value.i) + display->textureFilter = GL_NEAREST; + else + display->textureFilter = GL_LINEAR; + + return TRUE; + } + break; + case COMP_DISPLAY_OPTION_PING_DELAY: + if (compSetIntOption (o, value)) + { + if (display->pingHandle) + compRemoveTimeout (display->pingHandle); + + display->pingHandle = + compAddTimeout (o->value.i, o->value.i + 500, + pingTimeout, display); + return TRUE; + } + break; + case COMP_DISPLAY_OPTION_AUDIBLE_BELL: + if (compSetBoolOption (o, value)) + { + setAudibleBell (display, o->value.b); + return TRUE; + } + break; + default: + if (compSetDisplayOption (display, o, value)) + return TRUE; + break; + } + + return FALSE; +} + +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]; + + /* The old plugin list always begins with the core plugin. To make sure + we don't unnecessarily unload plugins if the new plugin list does not + contain the core plugin, we have to use an offset */ + if (o->value.list.nValue > 0 && strcmp (o->value.list.value[0].s, "core")) + i = 0; + else + i = 1; + + /* j is initialized to 1 to make sure we never pop the core plugin */ + for (j = 1; j < d->plugin.list.nValue && i < o->value.list.nValue; i++, j++) + { + if (strcmp (d->plugin.list.value[j].s, o->value.list.value[i].s)) + break; + } + + nPop = d->plugin.list.nValue - j; + + if (nPop) + { + pop = malloc (sizeof (CompPlugin *) * nPop); + if (!pop) + { + (*core.setOptionForPlugin) (&d->base, "core", 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 (CompOptionValue) * + (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); + + (*core.setOptionForPlugin) (&d->base, "core", o->name, &d->plugin); +} + +static void +addTimeout (CompTimeout *timeout) +{ + CompTimeout *p = 0, *t; + + for (t = core.timeouts; t; t = t->next) + { + if (timeout->minTime < t->minLeft) + break; + + p = t; + } + + timeout->next = t; + timeout->minLeft = timeout->minTime; + timeout->maxLeft = timeout->maxTime; + + if (p) + p->next = timeout; + else + core.timeouts = timeout; +} + +CompTimeoutHandle +compAddTimeout (int minTime, + int maxTime, + CallBackProc callBack, + void *closure) +{ + CompTimeout *timeout; + + timeout = malloc (sizeof (CompTimeout)); + if (!timeout) + return 0; + + timeout->minTime = minTime; + timeout->maxTime = (maxTime >= minTime) ? maxTime : minTime; + timeout->callBack = callBack; + timeout->closure = closure; + timeout->handle = core.lastTimeoutHandle++; + + if (core.lastTimeoutHandle == MAXSHORT) + core.lastTimeoutHandle = 1; + + addTimeout (timeout); + + return timeout->handle; +} + +void * +compRemoveTimeout (CompTimeoutHandle handle) +{ + CompTimeout *p = 0, *t; + void *closure = NULL; + + for (t = core.timeouts; t; t = t->next) + { + if (t->handle == handle) + break; + + p = t; + } + + if (t) + { + if (p) + p->next = t->next; + else + core.timeouts = t->next; + + closure = t->closure; + + free (t); + } + + return closure; +} + +CompWatchFdHandle +compAddWatchFd (int fd, + short int events, + CallBackProc callBack, + void *closure) +{ + CompWatchFd *watchFd; + + watchFd = malloc (sizeof (CompWatchFd)); + if (!watchFd) + return 0; + + watchFd->fd = fd; + watchFd->callBack = callBack; + watchFd->closure = closure; + watchFd->handle = core.lastWatchFdHandle++; + + if (core.lastWatchFdHandle == MAXSHORT) + core.lastWatchFdHandle = 1; + + watchFd->next = core.watchFds; + core.watchFds = watchFd; + + core.nWatchFds++; + + core.watchPollFds = realloc (core.watchPollFds, + core.nWatchFds * sizeof (struct pollfd)); + + core.watchPollFds[core.nWatchFds - 1].fd = fd; + core.watchPollFds[core.nWatchFds - 1].events = events; + + return watchFd->handle; +} + +void +compRemoveWatchFd (CompWatchFdHandle handle) +{ + CompWatchFd *p = 0, *w; + int i; + + for (i = core.nWatchFds - 1, w = core.watchFds; w; i--, w = w->next) + { + if (w->handle == handle) + break; + + p = w; + } + + if (w) + { + if (p) + p->next = w->next; + else + core.watchFds = w->next; + + core.nWatchFds--; + + if (i < core.nWatchFds) + memmove (&core.watchPollFds[i], &core.watchPollFds[i + 1], + (core.nWatchFds - i) * sizeof (struct pollfd)); + + free (w); + } +} + +short int +compWatchFdEvents (CompWatchFdHandle handle) +{ + CompWatchFd *w; + int i; + + for (i = core.nWatchFds - 1, w = core.watchFds; w; i--, w = w->next) + if (w->handle == handle) + return core.watchPollFds[i].revents; + + return 0; +} + +#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 *tv, + struct timeval *lastTv, + Bool idle) +{ + int diff, next; + + diff = TIMEVALDIFF (tv, lastTv); + + /* handle clock rollback */ + if (diff < 0) + diff = 0; + + if (idle || + (s->getVideoSync && s->opt[COMP_SCREEN_OPTION_SYNC_TO_VBLANK].value.b)) + { + if (s->timeMult > 1) + { + s->frameStatus = -1; + s->redrawTime = s->optimalRedrawTime; + s->timeMult--; + } + } + else + { + if (diff > s->redrawTime) + { + if (s->frameStatus > 0) + s->frameStatus = 0; + + next = s->optimalRedrawTime * (s->timeMult + 1); + if (diff > next) + { + s->frameStatus--; + if (s->frameStatus < -1) + { + s->timeMult++; + s->redrawTime = diff = next; + } + } + } + else if (diff < s->redrawTime) + { + if (s->frameStatus < 0) + s->frameStatus = 0; + + if (s->timeMult > 1) + { + next = s->optimalRedrawTime * (s->timeMult - 1); + if (diff < next) + { + s->frameStatus++; + if (s->frameStatus > 4) + { + s->timeMult--; + s->redrawTime = next; + } + } + } + } + } + + if (diff > s->redrawTime) + return 0; + + return s->redrawTime - diff; +} + +static const int maskTable[] = { + ShiftMask, LockMask, ControlMask, Mod1Mask, + Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask +}; +static const int maskTableSize = sizeof (maskTable) / sizeof (int); + +void +updateModifierMappings (CompDisplay *d) +{ + unsigned int modMask[CompModNum]; + int i, minKeycode, maxKeycode, keysymsPerKeycode = 0; + KeySym* key; + + for (i = 0; i < CompModNum; i++) + modMask[i] = 0; + + XDisplayKeycodes (d->display, &minKeycode, &maxKeycode); + key = XGetKeyboardMapping (d->display, + minKeycode, (maxKeycode - minKeycode + 1), + &keysymsPerKeycode); + + if (d->modMap) + XFreeModifiermap (d->modMap); + + d->modMap = XGetModifierMapping (d->display); + if (d->modMap && d->modMap->max_keypermod > 0) + { + KeySym keysym; + int index, size, mask; + + size = maskTableSize * d->modMap->max_keypermod; + + for (i = 0; i < size; i++) + { + if (!d->modMap->modifiermap[i]) + continue; + + index = 0; + do + { + keysym = XKeycodeToKeysym (d->display, + d->modMap->modifiermap[i], + index++); + } while (!keysym && index < keysymsPerKeycode); + + if (keysym) + { + mask = maskTable[i / d->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; + } + } + } + + for (i = 0; i < CompModNum; i++) + { + if (!modMask[i]) + modMask[i] = CompNoMask; + } + + if (memcmp (modMask, d->modMask, sizeof (modMask))) + { + CompScreen *s; + + memcpy (d->modMask, modMask, sizeof (modMask)); + + d->ignoredModMask = LockMask | + (modMask[CompModNumLock] & ~CompNoMask) | + (modMask[CompModScrollLock] & ~CompNoMask); + + for (s = d->screens; s; s = s->next) + updatePassiveGrabs (s); + } + } + + if (key) + XFree (key); +} + +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; +} + +unsigned int +keycodeToModifiers (CompDisplay *d, + int keycode) +{ + unsigned int mods = 0; + int mod, k; + + for (mod = 0; mod < maskTableSize; mod++) + { + for (k = 0; k < d->modMap->max_keypermod; k++) + { + if (d->modMap->modifiermap[mod * d->modMap->max_keypermod + k] == + keycode) + mods |= maskTable[mod]; + } + } + + return mods; +} + +static int +doPoll (int timeout) +{ + int rv; + + rv = poll (core.watchPollFds, core.nWatchFds, timeout); + if (rv) + { + CompWatchFd *w; + int i; + + for (i = core.nWatchFds - 1, w = core.watchFds; w; i--, w = w->next) + { + if (core.watchPollFds[i].revents != 0 && w->callBack) + (*w->callBack) (w->closure); + } + } + + return rv; +} + +static void +handleTimeouts (struct timeval *tv) +{ + CompTimeout *t; + int timeDiff; + + timeDiff = TIMEVALDIFF (tv, &core.lastTimeout); + + /* handle clock rollback */ + if (timeDiff < 0) + timeDiff = 0; + + for (t = core.timeouts; t; t = t->next) + { + t->minLeft -= timeDiff; + t->maxLeft -= timeDiff; + } + + while (core.timeouts && core.timeouts->minLeft <= 0) + { + t = core.timeouts; + if ((*t->callBack) (t->closure)) + { + core.timeouts = t->next; + addTimeout (t); + } + else + { + core.timeouts = t->next; + free (t); + } + } + + core.lastTimeout = *tv; +} + +static void +waitForVideoSync (CompScreen *s) +{ + unsigned int sync; + + if (!s->opt[COMP_SCREEN_OPTION_SYNC_TO_VBLANK].value.b) + return; + + if (s->getVideoSync) + { + glFlush (); + + (*s->getVideoSync) (&sync); + (*s->waitVideoSync) (2, (sync + 1) % 2, &sync); + } +} + + +void +paintScreen (CompScreen *s, + CompOutput *outputs, + int numOutput, + unsigned int mask) +{ + XRectangle r; + int i; + + for (i = 0; i < numOutput; i++) + { + targetScreen = s; + targetOutput = &outputs[i]; + + r.x = outputs[i].region.extents.x1; + r.y = s->height - outputs[i].region.extents.y2; + r.width = outputs[i].width; + r.height = outputs[i].height; + + if (s->lastViewport.x != r.x || + s->lastViewport.y != r.y || + s->lastViewport.width != r.width || + s->lastViewport.height != r.height) + { + glViewport (r.x, r.y, r.width, r.height); + s->lastViewport = r; + } + + if (mask & COMP_SCREEN_DAMAGE_ALL_MASK) + { + (*s->paintOutput) (s, + &defaultScreenPaintAttrib, + &identity, + &outputs[i].region, &outputs[i], + PAINT_SCREEN_REGION_MASK | + PAINT_SCREEN_FULL_MASK); + } + else if (mask & COMP_SCREEN_DAMAGE_REGION_MASK) + { + XIntersectRegion (core.tmpRegion, + &outputs[i].region, + core.outputRegion); + + if (!(*s->paintOutput) (s, + &defaultScreenPaintAttrib, + &identity, + core.outputRegion, &outputs[i], + PAINT_SCREEN_REGION_MASK)) + { + (*s->paintOutput) (s, + &defaultScreenPaintAttrib, + &identity, + &outputs[i].region, &outputs[i], + PAINT_SCREEN_FULL_MASK); + + XUnionRegion (core.tmpRegion, + &outputs[i].region, + core.tmpRegion); + + } + } + } +} + +void +eventLoop (void) +{ + XEvent event; + int timeDiff; + struct timeval tv; + CompDisplay *d; + CompScreen *s; + CompWindow *w; + CompTimeout *t; + int time, timeToNextRedraw = 0; + unsigned int damageMask, mask; + + for (d = core.displays; d; d = d->next) + d->watchFdHandle = + compAddWatchFd (ConnectionNumber (d->display), POLLIN, NULL, NULL); + + for (;;) + { + if (restartSignal || shutDown) + break; + + for (d = core.displays; d; d = d->next) + { + if (d->dirtyPluginList) + updatePlugins (d); + + while (XPending (d->display)) + { + XNextEvent (d->display, &event); + + switch (event.type) { + case ButtonPress: + case ButtonRelease: + pointerX = event.xbutton.x_root; + pointerY = event.xbutton.y_root; + break; + case KeyPress: + case KeyRelease: + pointerX = event.xkey.x_root; + pointerY = event.xkey.y_root; + break; + case MotionNotify: + pointerX = event.xmotion.x_root; + pointerY = event.xmotion.y_root; + break; + case EnterNotify: + case LeaveNotify: + pointerX = event.xcrossing.x_root; + pointerY = event.xcrossing.y_root; + break; + case ClientMessage: + if (event.xclient.message_type == d->xdndPositionAtom) + { + pointerX = event.xclient.data.l[2] >> 16; + pointerY = event.xclient.data.l[2] & 0xffff; + } + default: + break; + } + + sn_display_process_event (d->snDisplay, &event); + + inHandleEvent = TRUE; + + (*d->handleEvent) (d, &event); + + inHandleEvent = FALSE; + + lastPointerX = pointerX; + lastPointerY = pointerY; + } + } + + for (d = core.displays; d; d = d->next) + { + for (s = d->screens; s; s = s->next) + { + if (s->damageMask) + { + finishScreenDrawing (s); + } + else + { + s->idle = TRUE; + } + } + } + + damageMask = 0; + timeToNextRedraw = MAXSHORT; + + for (d = core.displays; d; d = d->next) + { + for (s = d->screens; s; s = s->next) + { + if (!s->damageMask) + continue; + + if (!damageMask) + { + gettimeofday (&tv, 0); + damageMask |= s->damageMask; + } + + s->timeLeft = getTimeToNextRedraw (s, &tv, &s->lastRedraw, + s->idle); + if (s->timeLeft < timeToNextRedraw) + timeToNextRedraw = s->timeLeft; + } + } + + if (damageMask) + { + time = timeToNextRedraw; + if (time) + time = doPoll (time); + + if (time == 0) + { + gettimeofday (&tv, 0); + + if (core.timeouts) + handleTimeouts (&tv); + + for (d = core.displays; d; d = d->next) + { + for (s = d->screens; s; s = s->next) + { + if (!s->damageMask || s->timeLeft > timeToNextRedraw) + continue; + + targetScreen = s; + + timeDiff = TIMEVALDIFF (&tv, &s->lastRedraw); + + /* handle clock rollback */ + if (timeDiff < 0) + timeDiff = 0; + + makeScreenCurrent (s); + + if (s->slowAnimations) + { + (*s->preparePaintScreen) (s, + s->idle ? 2 : + (timeDiff * 2) / + s->redrawTime); + } + else + (*s->preparePaintScreen) (s, + s->idle ? s->redrawTime : + timeDiff); + + /* substract top most overlay window region */ + if (s->overlayWindowCount) + { + for (w = s->reverseWindows; w; w = w->prev) + { + if (w->destroyed || w->invisible) + continue; + + if (!w->redirected) + XSubtractRegion (s->damage, w->region, + s->damage); + + break; + } + + if (s->damageMask & COMP_SCREEN_DAMAGE_ALL_MASK) + { + s->damageMask &= ~COMP_SCREEN_DAMAGE_ALL_MASK; + s->damageMask |= + COMP_SCREEN_DAMAGE_REGION_MASK; + } + } + + if (s->damageMask & COMP_SCREEN_DAMAGE_REGION_MASK) + { + XIntersectRegion (s->damage, &s->region, + core.tmpRegion); + + if (core.tmpRegion->numRects == 1 && + core.tmpRegion->rects->x1 == 0 && + core.tmpRegion->rects->y1 == 0 && + core.tmpRegion->rects->x2 == s->width && + core.tmpRegion->rects->y2 == s->height) + damageScreen (s); + } + + EMPTY_REGION (s->damage); + + mask = s->damageMask; + s->damageMask = 0; + + if (s->clearBuffers) + { + if (mask & COMP_SCREEN_DAMAGE_ALL_MASK) + glClear (GL_COLOR_BUFFER_BIT); + } + + if (s->opt[COMP_SCREEN_OPTION_FORCE_INDEPENDENT].value.b + || !s->hasOverlappingOutputs) + (*s->paintScreen) (s, s->outputDev, + s->nOutputDev, + mask); + else + (*s->paintScreen) (s, &s->fullscreenOutput, 1, + mask); + + targetScreen = NULL; + targetOutput = &s->outputDev[0]; + + waitForVideoSync (s); + + if (mask & COMP_SCREEN_DAMAGE_ALL_MASK) + { + glXSwapBuffers (d->display, s->output); + } + else + { + BoxPtr pBox; + int nBox, y; + + pBox = core.tmpRegion->rects; + nBox = core.tmpRegion->numRects; + + if (s->copySubBuffer) + { + while (nBox--) + { + y = s->height - pBox->y2; + + (*s->copySubBuffer) (d->display, + s->output, + pBox->x1, y, + pBox->x2 - pBox->x1, + pBox->y2 - pBox->y1); + + pBox++; + } + } + else + { + glEnable (GL_SCISSOR_TEST); + glDrawBuffer (GL_FRONT); + + 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 (); + } + } + + 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--; + } + + s->idle = FALSE; + } + } + } + } + else + { + if (core.timeouts) + { + if (core.timeouts->minLeft > 0) + { + t = core.timeouts; + time = t->maxLeft; + while (t && t->minLeft <= time) + { + if (t->maxLeft < time) + time = t->maxLeft; + t = t->next; + } + doPoll (time); + } + + gettimeofday (&tv, 0); + + handleTimeouts (&tv); + } + else + { + doPoll (-1); + } + } + } + + for (d = core.displays; d; d = d->next) + compRemoveWatchFd (d->watchFdHandle); +} + +static int errors = 0; + +static int +errorHandler (Display *dpy, + XErrorEvent *e) +{ + +#ifdef DEBUG + char str[128]; +#endif + + errors++; + +#ifdef DEBUG + XGetErrorDatabaseText (dpy, "XlibMessage", "XError", "", str, 128); + fprintf (stderr, "%s", str); + + XGetErrorText (dpy, e->error_code, str, 128); + fprintf (stderr, ": %s\n ", str); + + XGetErrorDatabaseText (dpy, "XlibMessage", "MajorCode", "%d", str, 128); + fprintf (stderr, str, e->request_code); + + sprintf (str, "%d", e->request_code); + XGetErrorDatabaseText (dpy, "XRequest", str, "", str, 128); + if (strcmp (str, "")) + fprintf (stderr, " (%s)", str); + fprintf (stderr, "\n "); + + XGetErrorDatabaseText (dpy, "XlibMessage", "MinorCode", "%d", str, 128); + fprintf (stderr, str, e->minor_code); + fprintf (stderr, "\n "); + + XGetErrorDatabaseText (dpy, "XlibMessage", "ResourceID", "%d", str, 128); + fprintf (stderr, str, e->resourceid); + fprintf (stderr, "\n"); + + /* abort (); */ +#endif + + return 0; +} + +int +compCheckForError (Display *dpy) +{ + int e; + + XSync (dpy, FALSE); + + e = errors; + errors = 0; + + return e; +} + +/* add actions that should be automatically added as no screens + existed when they were initialized. */ +static void +addScreenActions (CompScreen *s) +{ + int i; + + for (i = 0; i < COMP_DISPLAY_OPTION_NUM; i++) + { + if (!isActionOption (&s->display->opt[i])) + continue; + + if (s->display->opt[i].value.action.state & CompActionStateAutoGrab) + addScreenAction (s, &s->display->opt[i].value.action); + } +} + +void +addScreenToDisplay (CompDisplay *display, + CompScreen *s) +{ + CompScreen *prev; + + for (prev = display->screens; prev && prev->next; prev = prev->next); + + if (prev) + prev->next = s; + else + display->screens = s; + + addScreenActions (s); +} + +static void +freeDisplay (CompDisplay *d) +{ + compFiniDisplayOptions (d, d->opt, COMP_DISPLAY_OPTION_NUM); + + compFiniOptionValue (&d->plugin, CompOptionTypeList); + + if (d->modMap) + XFreeModifiermap (d->modMap); + + if (d->screenInfo) + XFree (d->screenInfo); + + if (d->screenPrivateIndices) + free (d->screenPrivateIndices); + + if (d->base.privates) + free (d->base.privates); + + free (d); +} + +Bool +addDisplay (const char *name) +{ + CompDisplay *d; + CompPrivate *privates; + Display *dpy; + Window focus; + int revertTo, i; + int compositeMajor, compositeMinor; + int fixesMinor; + int xkbOpcode; + int firstScreen, lastScreen; + + d = malloc (sizeof (CompDisplay)); + if (!d) + return FALSE; + + if (displayPrivateLen) + { + privates = malloc (displayPrivateLen * sizeof (CompPrivate)); + if (!privates) + { + free (d); + return FALSE; + } + } + else + privates = 0; + + compObjectInit (&d->base, privates, COMP_OBJECT_TYPE_DISPLAY); + + d->next = NULL; + d->screens = NULL; + + d->watchFdHandle = 0; + + d->screenPrivateIndices = 0; + d->screenPrivateLen = 0; + + d->edgeDelayHandle = 0; + + d->modMap = 0; + + for (i = 0; i < CompModNum; i++) + d->modMask[i] = CompNoMask; + + d->ignoredModMask = LockMask; + + compInitOptionValue (&d->plugin); + + d->plugin.list.type = CompOptionTypeString; + d->plugin.list.nValue = 1; + d->plugin.list.value = malloc (sizeof (CompOptionValue)); + + if (!d->plugin.list.value) { + free (d); + return FALSE; + } + + d->plugin.list.value->s = strdup ("core"); + if (!d->plugin.list.value->s) { + free (d->plugin.list.value); + free (d); + return FALSE; + } + + d->dirtyPluginList = TRUE; + + d->textureFilter = GL_LINEAR; + d->below = None; + + d->activeWindow = 0; + + d->autoRaiseHandle = 0; + d->autoRaiseWindow = None; + + d->display = dpy = XOpenDisplay (name); + if (!d->display) + { + compLogMessage ("core", CompLogLevelFatal, + "Couldn't open display %s", XDisplayName (name)); + return FALSE; + } + + if (!compInitDisplayOptionsFromMetadata (d, + &coreMetadata, + coreDisplayOptionInfo, + d->opt, + COMP_DISPLAY_OPTION_NUM)) + return FALSE; + + d->opt[COMP_DISPLAY_OPTION_ABI].value.i = CORE_ABIVERSION; + + snprintf (d->displayString, 255, "DISPLAY=%s", DisplayString (dpy)); + +#ifdef DEBUG + XSynchronize (dpy, TRUE); +#endif + + XSetErrorHandler (errorHandler); + + updateModifierMappings (d); + + d->handleEvent = handleEvent; + d->handleCompizEvent = handleCompizEvent; + + d->fileToImage = fileToImage; + d->imageToFile = imageToFile; + + d->matchInitExp = matchInitExp; + d->matchExpHandlerChanged = matchExpHandlerChanged; + d->matchPropertyChanged = matchPropertyChanged; + + d->supportedAtom = XInternAtom (dpy, "_NET_SUPPORTED", 0); + d->supportingWmCheckAtom = XInternAtom (dpy, "_NET_SUPPORTING_WM_CHECK", 0); + + d->utf8StringAtom = XInternAtom (dpy, "UTF8_STRING", 0); + + d->wmNameAtom = XInternAtom (dpy, "_NET_WM_NAME", 0); + + d->winTypeAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE", 0); + d->winTypeDesktopAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_DESKTOP", + 0); + d->winTypeDockAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_DOCK", 0); + d->winTypeToolbarAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_TOOLBAR", + 0); + d->winTypeMenuAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_MENU", 0); + d->winTypeUtilAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_UTILITY", + 0); + d->winTypeSplashAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_SPLASH", 0); + d->winTypeDialogAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_DIALOG", 0); + d->winTypeNormalAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_NORMAL", 0); + + d->winTypeDropdownMenuAtom = + XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU", 0); + d->winTypePopupMenuAtom = + XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_POPUP_MENU", 0); + d->winTypeTooltipAtom = + XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_TOOLTIP", 0); + d->winTypeNotificationAtom = + XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_NOTIFICATION", 0); + d->winTypeComboAtom = + XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_COMBO", 0); + d->winTypeDndAtom = + XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_DND", 0); + + d->winOpacityAtom = XInternAtom (dpy, "_NET_WM_WINDOW_OPACITY", 0); + d->winBrightnessAtom = XInternAtom (dpy, "_NET_WM_WINDOW_BRIGHTNESS", 0); + d->winSaturationAtom = XInternAtom (dpy, "_NET_WM_WINDOW_SATURATION", 0); + + d->winActiveAtom = XInternAtom (dpy, "_NET_ACTIVE_WINDOW", 0); + + d->winDesktopAtom = XInternAtom (dpy, "_NET_WM_DESKTOP", 0); + + d->workareaAtom = XInternAtom (dpy, "_NET_WORKAREA", 0); + + d->desktopViewportAtom = XInternAtom (dpy, "_NET_DESKTOP_VIEWPORT", 0); + d->desktopGeometryAtom = XInternAtom (dpy, "_NET_DESKTOP_GEOMETRY", 0); + d->currentDesktopAtom = XInternAtom (dpy, "_NET_CURRENT_DESKTOP", 0); + d->numberOfDesktopsAtom = XInternAtom (dpy, "_NET_NUMBER_OF_DESKTOPS", 0); + + d->winStateAtom = XInternAtom (dpy, "_NET_WM_STATE", 0); + d->winStateModalAtom = + XInternAtom (dpy, "_NET_WM_STATE_MODAL", 0); + d->winStateStickyAtom = + XInternAtom (dpy, "_NET_WM_STATE_STICKY", 0); + d->winStateMaximizedVertAtom = + XInternAtom (dpy, "_NET_WM_STATE_MAXIMIZED_VERT", 0); + d->winStateMaximizedHorzAtom = + XInternAtom (dpy, "_NET_WM_STATE_MAXIMIZED_HORZ", 0); + d->winStateShadedAtom = + XInternAtom (dpy, "_NET_WM_STATE_SHADED", 0); + d->winStateSkipTaskbarAtom = + XInternAtom (dpy, "_NET_WM_STATE_SKIP_TASKBAR", 0); + d->winStateSkipPagerAtom = + XInternAtom (dpy, "_NET_WM_STATE_SKIP_PAGER", 0); + d->winStateHiddenAtom = + XInternAtom (dpy, "_NET_WM_STATE_HIDDEN", 0); + d->winStateFullscreenAtom = + XInternAtom (dpy, "_NET_WM_STATE_FULLSCREEN", 0); + d->winStateAboveAtom = + XInternAtom (dpy, "_NET_WM_STATE_ABOVE", 0); + d->winStateBelowAtom = + XInternAtom (dpy, "_NET_WM_STATE_BELOW", 0); + d->winStateDemandsAttentionAtom = + XInternAtom (dpy, "_NET_WM_STATE_DEMANDS_ATTENTION", 0); + d->winStateDisplayModalAtom = + XInternAtom (dpy, "_NET_WM_STATE_DISPLAY_MODAL", 0); + + d->winActionMoveAtom = XInternAtom (dpy, "_NET_WM_ACTION_MOVE", 0); + d->winActionResizeAtom = + XInternAtom (dpy, "_NET_WM_ACTION_RESIZE", 0); + d->winActionStickAtom = + XInternAtom (dpy, "_NET_WM_ACTION_STICK", 0); + d->winActionMinimizeAtom = + XInternAtom (dpy, "_NET_WM_ACTION_MINIMIZE", 0); + d->winActionMaximizeHorzAtom = + XInternAtom (dpy, "_NET_WM_ACTION_MAXIMIZE_HORZ", 0); + d->winActionMaximizeVertAtom = + XInternAtom (dpy, "_NET_WM_ACTION_MAXIMIZE_VERT", 0); + d->winActionFullscreenAtom = + XInternAtom (dpy, "_NET_WM_ACTION_FULLSCREEN", 0); + d->winActionCloseAtom = + XInternAtom (dpy, "_NET_WM_ACTION_CLOSE", 0); + d->winActionShadeAtom = + XInternAtom (dpy, "_NET_WM_ACTION_SHADE", 0); + d->winActionChangeDesktopAtom = + XInternAtom (dpy, "_NET_WM_ACTION_CHANGE_DESKTOP", 0); + d->winActionAboveAtom = + XInternAtom (dpy, "_NET_WM_ACTION_ABOVE", 0); + d->winActionBelowAtom = + XInternAtom (dpy, "_NET_WM_ACTION_BELOW", 0); + + d->wmAllowedActionsAtom = XInternAtom (dpy, "_NET_WM_ALLOWED_ACTIONS", 0); + + d->wmStrutAtom = XInternAtom (dpy, "_NET_WM_STRUT", 0); + d->wmStrutPartialAtom = XInternAtom (dpy, "_NET_WM_STRUT_PARTIAL", 0); + + d->wmUserTimeAtom = XInternAtom (dpy, "_NET_WM_USER_TIME", 0); + + d->wmIconAtom = XInternAtom (dpy,"_NET_WM_ICON", 0); + d->wmIconGeometryAtom = XInternAtom (dpy, "_NET_WM_ICON_GEOMETRY", 0); + + d->clientListAtom = XInternAtom (dpy, "_NET_CLIENT_LIST", 0); + d->clientListStackingAtom = + XInternAtom (dpy, "_NET_CLIENT_LIST_STACKING", 0); + + d->frameExtentsAtom = XInternAtom (dpy, "_NET_FRAME_EXTENTS", 0); + d->frameWindowAtom = XInternAtom (dpy, "_NET_FRAME_WINDOW", 0); + + d->wmStateAtom = XInternAtom (dpy, "WM_STATE", 0); + d->wmChangeStateAtom = XInternAtom (dpy, "WM_CHANGE_STATE", 0); + d->wmProtocolsAtom = XInternAtom (dpy, "WM_PROTOCOLS", 0); + d->wmClientLeaderAtom = XInternAtom (dpy, "WM_CLIENT_LEADER", 0); + + d->wmDeleteWindowAtom = XInternAtom (dpy, "WM_DELETE_WINDOW", 0); + d->wmTakeFocusAtom = XInternAtom (dpy, "WM_TAKE_FOCUS", 0); + d->wmPingAtom = XInternAtom (dpy, "_NET_WM_PING", 0); + + d->wmSyncRequestAtom = XInternAtom (dpy, "_NET_WM_SYNC_REQUEST", 0); + d->wmSyncRequestCounterAtom = + XInternAtom (dpy, "_NET_WM_SYNC_REQUEST_COUNTER", 0); + + d->wmFullscreenMonitorsAtom = + XInternAtom (dpy, "_NET_WM_FULLSCREEN_MONITORS", 0); + + d->closeWindowAtom = XInternAtom (dpy, "_NET_CLOSE_WINDOW", 0); + d->wmMoveResizeAtom = XInternAtom (dpy, "_NET_WM_MOVERESIZE", 0); + d->moveResizeWindowAtom = XInternAtom (dpy, "_NET_MOVERESIZE_WINDOW", 0); + d->restackWindowAtom = XInternAtom (dpy, "_NET_RESTACK_WINDOW", 0); + + d->showingDesktopAtom = XInternAtom (dpy, "_NET_SHOWING_DESKTOP", 0); + + d->xBackgroundAtom[0] = XInternAtom (dpy, "_XSETROOT_ID", 0); + d->xBackgroundAtom[1] = XInternAtom (dpy, "_XROOTPMAP_ID", 0); + + d->toolkitActionAtom = + XInternAtom (dpy, "_COMPIZ_TOOLKIT_ACTION", 0); + d->toolkitActionWindowMenuAtom = + XInternAtom (dpy, "_COMPIZ_TOOLKIT_ACTION_WINDOW_MENU", 0); + d->toolkitActionForceQuitDialogAtom = + XInternAtom (dpy, "_COMPIZ_TOOLKIT_ACTION_FORCE_QUIT_DIALOG", 0); + + d->mwmHintsAtom = XInternAtom (dpy, "_MOTIF_WM_HINTS", 0); + + d->xdndAwareAtom = XInternAtom (dpy, "XdndAware", 0); + d->xdndEnterAtom = XInternAtom (dpy, "XdndEnter", 0); + d->xdndLeaveAtom = XInternAtom (dpy, "XdndLeave", 0); + d->xdndPositionAtom = XInternAtom (dpy, "XdndPosition", 0); + d->xdndStatusAtom = XInternAtom (dpy, "XdndStatus", 0); + d->xdndDropAtom = XInternAtom (dpy, "XdndDrop", 0); + + d->managerAtom = XInternAtom (dpy, "MANAGER", 0); + d->targetsAtom = XInternAtom (dpy, "TARGETS", 0); + d->multipleAtom = XInternAtom (dpy, "MULTIPLE", 0); + d->timestampAtom = XInternAtom (dpy, "TIMESTAMP", 0); + d->versionAtom = XInternAtom (dpy, "VERSION", 0); + d->atomPairAtom = XInternAtom (dpy, "ATOM_PAIR", 0); + + d->startupIdAtom = XInternAtom (dpy, "_NET_STARTUP_ID", 0); + + d->snDisplay = sn_display_new (dpy, NULL, NULL); + if (!d->snDisplay) + return FALSE; + + d->lastPing = 1; + + if (!XQueryExtension (dpy, + COMPOSITE_NAME, + &d->compositeOpcode, + &d->compositeEvent, + &d->compositeError)) + { + compLogMessage ("core", CompLogLevelFatal, + "No composite extension"); + return FALSE; + } + + XCompositeQueryVersion (dpy, &compositeMajor, &compositeMinor); + if (compositeMajor == 0 && compositeMinor < 2) + { + compLogMessage ("core", CompLogLevelFatal, + "Old composite extension"); + return FALSE; + } + + if (!XDamageQueryExtension (dpy, &d->damageEvent, &d->damageError)) + { + compLogMessage ("core", CompLogLevelFatal, + "No damage extension"); + return FALSE; + } + + if (!XSyncQueryExtension (dpy, &d->syncEvent, &d->syncError)) + { + compLogMessage ("core", CompLogLevelFatal, + "No sync extension"); + return FALSE; + } + + if (!XFixesQueryExtension (dpy, &d->fixesEvent, &d->fixesError)) + { + compLogMessage ("core", CompLogLevelFatal, + "No fixes extension"); + return FALSE; + } + + XFixesQueryVersion (dpy, &d->fixesVersion, &fixesMinor); + /* + if (d->fixesVersion < 5) + { + fprintf (stderr, "%s: Need fixes extension version 5 or later " + "for client-side cursor\n", programName); + } + */ + + d->randrExtension = XRRQueryExtension (dpy, + &d->randrEvent, + &d->randrError); + + d->shapeExtension = XShapeQueryExtension (dpy, + &d->shapeEvent, + &d->shapeError); + + d->xkbExtension = XkbQueryExtension (dpy, + &xkbOpcode, + &d->xkbEvent, + &d->xkbError, + NULL, NULL); + if (d->xkbExtension) + { + XkbSelectEvents (dpy, + XkbUseCoreKbd, + XkbBellNotifyMask | XkbStateNotifyMask, + XkbAllEventsMask); + } + else + { + compLogMessage ("core", CompLogLevelFatal, + "No XKB extension"); + + d->xkbEvent = d->xkbError = -1; + } + + d->screenInfo = NULL; + d->nScreenInfo = 0; + + d->xineramaExtension = XineramaQueryExtension (dpy, + &d->xineramaEvent, + &d->xineramaError); + + if (d->xineramaExtension) + d->screenInfo = XineramaQueryScreens (dpy, &d->nScreenInfo); + + d->escapeKeyCode = XKeysymToKeycode (dpy, XStringToKeysym ("Escape")); + d->returnKeyCode = XKeysymToKeycode (dpy, XStringToKeysym ("Return")); + + addDisplayToCore (d); + + /* TODO: bailout properly when objectInitPlugins fails */ + assert (objectInitPlugins (&d->base)); + + (*core.objectAdd) (&core.base, &d->base); + + if (onlyCurrentScreen) + { + firstScreen = DefaultScreen (dpy); + lastScreen = DefaultScreen (dpy); + } + else + { + firstScreen = 0; + lastScreen = ScreenCount (dpy) - 1; + } + + for (i = firstScreen; i <= lastScreen; i++) + { + Window newWmSnOwner = None, newCmSnOwner = None; + Atom wmSnAtom = 0, cmSnAtom = 0; + Time wmSnTimestamp = 0; + XEvent event; + XSetWindowAttributes attr; + Window currentWmSnOwner, currentCmSnOwner; + char buf[128]; + Window rootDummy, childDummy; + unsigned int uDummy; + int x, y, dummy; + + sprintf (buf, "WM_S%d", i); + wmSnAtom = XInternAtom (dpy, buf, 0); + + currentWmSnOwner = XGetSelectionOwner (dpy, wmSnAtom); + + if (currentWmSnOwner != None) + { + if (!replaceCurrentWm) + { + compLogMessage ("core", CompLogLevelError, + "Screen %d on display \"%s\" already " + "has a window manager; try using the " + "--replace option to replace the current " + "window manager.", + i, DisplayString (dpy)); + + continue; + } + + XSelectInput (dpy, currentWmSnOwner, + StructureNotifyMask); + } + + sprintf (buf, "_NET_WM_CM_S%d", i); + cmSnAtom = XInternAtom (dpy, buf, 0); + + currentCmSnOwner = XGetSelectionOwner (dpy, cmSnAtom); + + if (currentCmSnOwner != None) + { + if (!replaceCurrentWm) + { + compLogMessage ("core", CompLogLevelError, + "Screen %d on display \"%s\" already " + "has a compositing manager; try using the " + "--replace option to replace the current " + "compositing manager.", + i, DisplayString (dpy)); + + continue; + } + } + + attr.override_redirect = TRUE; + attr.event_mask = PropertyChangeMask; + + newCmSnOwner = newWmSnOwner = + XCreateWindow (dpy, XRootWindow (dpy, i), + -100, -100, 1, 1, 0, + CopyFromParent, CopyFromParent, + CopyFromParent, + CWOverrideRedirect | CWEventMask, + &attr); + + XChangeProperty (dpy, + newWmSnOwner, + d->wmNameAtom, + d->utf8StringAtom, 8, + PropModeReplace, + (unsigned char *) PACKAGE, + strlen (PACKAGE)); + + XWindowEvent (dpy, + newWmSnOwner, + PropertyChangeMask, + &event); + + wmSnTimestamp = event.xproperty.time; + + XSetSelectionOwner (dpy, wmSnAtom, newWmSnOwner, wmSnTimestamp); + + if (XGetSelectionOwner (dpy, wmSnAtom) != newWmSnOwner) + { + compLogMessage ("core", CompLogLevelError, + "Could not acquire window manager " + "selection on screen %d display \"%s\"", + i, DisplayString (dpy)); + + XDestroyWindow (dpy, newWmSnOwner); + + continue; + } + + /* Send client message indicating that we are now the WM */ + event.xclient.type = ClientMessage; + event.xclient.window = XRootWindow (dpy, i); + event.xclient.message_type = d->managerAtom; + event.xclient.format = 32; + event.xclient.data.l[0] = wmSnTimestamp; + event.xclient.data.l[1] = wmSnAtom; + event.xclient.data.l[2] = 0; + event.xclient.data.l[3] = 0; + event.xclient.data.l[4] = 0; + + XSendEvent (dpy, XRootWindow (dpy, i), FALSE, + StructureNotifyMask, &event); + + /* Wait for old window manager to go away */ + if (currentWmSnOwner != None) + { + do { + XWindowEvent (dpy, currentWmSnOwner, + StructureNotifyMask, &event); + } while (event.type != DestroyNotify); + } + + compCheckForError (dpy); + + XCompositeRedirectSubwindows (dpy, XRootWindow (dpy, i), + CompositeRedirectManual); + + if (compCheckForError (dpy)) + { + compLogMessage ("core", CompLogLevelError, + "Another composite manager is already " + "running on screen: %d", i); + + continue; + } + + XSetSelectionOwner (dpy, cmSnAtom, newCmSnOwner, wmSnTimestamp); + + if (XGetSelectionOwner (dpy, cmSnAtom) != newCmSnOwner) + { + compLogMessage ("core", CompLogLevelError, + "Could not acquire compositing manager " + "selection on screen %d display \"%s\"", + i, DisplayString (dpy)); + + continue; + } + + XGrabServer (dpy); + + XSelectInput (dpy, XRootWindow (dpy, i), + SubstructureRedirectMask | + SubstructureNotifyMask | + StructureNotifyMask | + PropertyChangeMask | + LeaveWindowMask | + EnterWindowMask | + KeyPressMask | + KeyReleaseMask | + ButtonPressMask | + ButtonReleaseMask | + FocusChangeMask | + ExposureMask); + + if (compCheckForError (dpy)) + { + compLogMessage ("core", CompLogLevelError, + "Another window manager is " + "already running on screen: %d", i); + + XUngrabServer (dpy); + continue; + } + + if (!addScreen (d, i, newWmSnOwner, wmSnAtom, wmSnTimestamp)) + { + compLogMessage ("core", CompLogLevelError, + "Failed to manage screen: %d", i); + } + + if (XQueryPointer (dpy, XRootWindow (dpy, i), + &rootDummy, &childDummy, + &x, &y, &dummy, &dummy, &uDummy)) + { + lastPointerX = pointerX = x; + lastPointerY = pointerY = y; + } + + XUngrabServer (dpy); + } + + if (!d->screens) + { + compLogMessage ("core", CompLogLevelFatal, + "No manageable screens found on display %s", + XDisplayName (name)); + return FALSE; + } + + setAudibleBell (d, d->opt[COMP_DISPLAY_OPTION_AUDIBLE_BELL].value.b); + + XGetInputFocus (dpy, &focus, &revertTo); + + /* move input focus to root window so that we get a FocusIn event when + moving it to the default window */ + XSetInputFocus (dpy, d->screens->root, RevertToPointerRoot, CurrentTime); + + if (focus == None || focus == PointerRoot) + { + focusDefaultWindow (d->screens); + } + else + { + CompWindow *w; + + w = findWindowAtDisplay (d, focus); + if (w) + { + moveInputFocusToWindow (w); + } + else + focusDefaultWindow (d->screens); + } + + d->pingHandle = + compAddTimeout (d->opt[COMP_DISPLAY_OPTION_PING_DELAY].value.i, + d->opt[COMP_DISPLAY_OPTION_PING_DELAY].value.i + 500, + pingTimeout, d); + + return TRUE; +} + +void +removeDisplay (CompDisplay *d) +{ + CompDisplay *p; + + for (p = core.displays; p; p = p->next) + if (p->next == d) + break; + + if (p) + p->next = d->next; + else + core.displays = NULL; + + while (d->screens) + removeScreen (d->screens); + + (*core.objectRemove) (&core.base, &d->base); + + objectFiniPlugins (&d->base); + + compRemoveTimeout (d->pingHandle); + + if (d->snDisplay) + sn_display_unref (d->snDisplay); + + XSync (d->display, False); + XCloseDisplay (d->display); + + freeDisplay (d); +} + +Time +getCurrentTimeFromDisplay (CompDisplay *d) +{ + XEvent event; + + XChangeProperty (d->display, d->screens->grabWindow, + XA_PRIMARY, XA_STRING, 8, + PropModeAppend, NULL, 0); + XWindowEvent (d->display, d->screens->grabWindow, + PropertyChangeMask, + &event); + + return event.xproperty.time; +} + +CompScreen * +findScreenAtDisplay (CompDisplay *d, + Window root) +{ + CompScreen *s; + + for (s = d->screens; s; s = s->next) + { + if (s->root == root) + return s; + } + + return 0; +} + +void +forEachWindowOnDisplay (CompDisplay *display, + ForEachWindowProc proc, + void *closure) +{ + CompScreen *s; + + for (s = display->screens; s; s = s->next) + forEachWindowOnScreen (s, proc, closure); +} + +CompWindow * +findWindowAtDisplay (CompDisplay *d, + Window id) +{ + CompScreen *s; + CompWindow *w; + + for (s = d->screens; s; s = s->next) + { + w = findWindowAtScreen (s, id); + if (w) + return w; + } + + return 0; +} + +CompWindow * +findTopLevelWindowAtDisplay (CompDisplay *d, + Window id) +{ + CompScreen *s; + CompWindow *w; + + for (s = d->screens; s; s = s->next) + { + w = findTopLevelWindowAtScreen (s, id); + if (w) + return w; + } + + return 0; +} + +static CompScreen * +findScreenForSelection (CompDisplay *display, + Window owner, + Atom selection) +{ + CompScreen *s; + + for (s = display->screens; s; s = s->next) + { + if (s->wmSnSelectionWindow == owner && s->wmSnAtom == selection) + return s; + } + + return NULL; +} + +/* from fvwm2, Copyright Matthias Clasen, Dominik Vogt */ +static Bool +convertProperty (CompDisplay *display, + CompScreen *screen, + Window w, + Atom target, + Atom property) +{ + +#define N_TARGETS 4 + + Atom conversionTargets[N_TARGETS]; + long icccmVersion[] = { 2, 0 }; + + conversionTargets[0] = display->targetsAtom; + conversionTargets[1] = display->multipleAtom; + conversionTargets[2] = display->timestampAtom; + conversionTargets[3] = display->versionAtom; + + if (target == display->targetsAtom) + XChangeProperty (display->display, w, property, + XA_ATOM, 32, PropModeReplace, + (unsigned char *) conversionTargets, N_TARGETS); + else if (target == display->timestampAtom) + XChangeProperty (display->display, w, property, + XA_INTEGER, 32, PropModeReplace, + (unsigned char *) &screen->wmSnTimestamp, 1); + else if (target == display->versionAtom) + XChangeProperty (display->display, w, property, + XA_INTEGER, 32, PropModeReplace, + (unsigned char *) icccmVersion, 2); + else + return FALSE; + + /* Be sure the PropertyNotify has arrived so we + * can send SelectionNotify + */ + XSync (display->display, FALSE); + + return TRUE; +} + +/* from fvwm2, Copyright Matthias Clasen, Dominik Vogt */ +void +handleSelectionRequest (CompDisplay *display, + XEvent *event) +{ + XSelectionEvent reply; + CompScreen *screen; + + screen = findScreenForSelection (display, + event->xselectionrequest.owner, + event->xselectionrequest.selection); + if (!screen) + return; + + reply.type = SelectionNotify; + reply.display = display->display; + reply.requestor = event->xselectionrequest.requestor; + reply.selection = event->xselectionrequest.selection; + reply.target = event->xselectionrequest.target; + reply.property = None; + reply.time = event->xselectionrequest.time; + + if (event->xselectionrequest.target == display->multipleAtom) + { + if (event->xselectionrequest.property != None) + { + Atom type, *adata; + int i, format; + unsigned long num, rest; + unsigned char *data; + + if (XGetWindowProperty (display->display, + event->xselectionrequest.requestor, + event->xselectionrequest.property, + 0, 256, FALSE, + display->atomPairAtom, + &type, &format, &num, &rest, + &data) != Success) + return; + + /* FIXME: to be 100% correct, should deal with rest > 0, + * but since we have 4 possible targets, we will hardly ever + * meet multiple requests with a length > 8 + */ + adata = (Atom *) data; + i = 0; + while (i < (int) num) + { + if (!convertProperty (display, screen, + event->xselectionrequest.requestor, + adata[i], adata[i + 1])) + adata[i + 1] = None; + + i += 2; + } + + XChangeProperty (display->display, + event->xselectionrequest.requestor, + event->xselectionrequest.property, + display->atomPairAtom, + 32, PropModeReplace, data, num); + + if (data) + XFree (data); + } + } + else + { + if (event->xselectionrequest.property == None) + event->xselectionrequest.property = event->xselectionrequest.target; + + if (convertProperty (display, screen, + event->xselectionrequest.requestor, + event->xselectionrequest.target, + event->xselectionrequest.property)) + reply.property = event->xselectionrequest.property; + } + + XSendEvent (display->display, + event->xselectionrequest.requestor, + FALSE, 0L, (XEvent *) &reply); +} + +void +handleSelectionClear (CompDisplay *display, + XEvent *event) +{ + /* We need to unmanage the screen on which we lost the selection */ + CompScreen *screen; + + screen = findScreenForSelection (display, + event->xselectionclear.window, + event->xselectionclear.selection); + + if (screen) + shutDown = TRUE; +} + +void +warpPointer (CompScreen *s, + int dx, + int dy) +{ + CompDisplay *display = s->display; + XEvent event; + + pointerX += dx; + pointerY += dy; + + if (pointerX >= s->width) + pointerX = s->width - 1; + else if (pointerX < 0) + pointerX = 0; + + if (pointerY >= s->height) + pointerY = s->height - 1; + else if (pointerY < 0) + pointerY = 0; + + XWarpPointer (display->display, + None, s->root, + 0, 0, 0, 0, + pointerX, pointerY); + + XSync (display->display, FALSE); + + while (XCheckMaskEvent (display->display, + LeaveWindowMask | + EnterWindowMask | + PointerMotionMask, + &event)); + + if (!inHandleEvent) + { + lastPointerX = pointerX; + lastPointerY = pointerY; + } +} + +Bool +setDisplayAction (CompDisplay *display, + CompOption *o, + CompOptionValue *value) +{ + CompScreen *s; + + for (s = display->screens; s; s = s->next) + if (!addScreenAction (s, &value->action)) + break; + + if (s) + { + CompScreen *failed = s; + + for (s = display->screens; s && s != failed; s = s->next) + removeScreenAction (s, &value->action); + + return FALSE; + } + else + { + for (s = display->screens; s; s = s->next) + removeScreenAction (s, &o->value.action); + } + + if (compSetActionOption (o, value)) + return TRUE; + + return FALSE; +} + +void +clearTargetOutput (CompDisplay *display, + unsigned int mask) +{ + if (targetScreen) + clearScreenOutput (targetScreen, + targetOutput, + mask); +} + +#define HOME_IMAGEDIR ".compiz/images" + +Bool +readImageFromFile (CompDisplay *display, + const char *name, + int *width, + int *height, + void **data) +{ + Bool status; + int stride; + + status = (*display->fileToImage) (display, NULL, name, width, height, + &stride, data); + if (!status) + { + char *home; + + home = getenv ("HOME"); + if (home) + { + char *path; + + path = malloc (strlen (home) + strlen (HOME_IMAGEDIR) + 2); + if (path) + { + sprintf (path, "%s/%s", home, HOME_IMAGEDIR); + status = (*display->fileToImage) (display, path, name, + width, height, &stride, + data); + + free (path); + + if (status) + return TRUE; + } + } + + status = (*display->fileToImage) (display, IMAGEDIR, name, + width, height, &stride, data); + } + + return status; +} + +Bool +writeImageToFile (CompDisplay *display, + const char *path, + const char *name, + const char *format, + int width, + int height, + void *data) +{ + return (*display->imageToFile) (display, path, name, format, width, height, + width * 4, data); +} + +Bool +fileToImage (CompDisplay *display, + const char *path, + const char *name, + int *width, + int *height, + int *stride, + void **data) +{ + return FALSE; +} + +Bool +imageToFile (CompDisplay *display, + const char *path, + const char *name, + const char *format, + int width, + int height, + int stride, + void *data) +{ + return FALSE; +} + +CompCursor * +findCursorAtDisplay (CompDisplay *display) +{ + CompScreen *s; + + for (s = display->screens; s; s = s->next) + if (s->cursors) + return s->cursors; + + return NULL; +} |