/* * 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 */ #ifdef HAVE_CONFIG_H # include "../config.h" #endif #include #include #include #include #include #define XK_MISCELLANY #include #include #include #include #include #include #include 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", "string", 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", "1000", 0, 0 }, { "edge_delay", "int", "0", 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); if (alwaysSwap == TRUE) damageScreen (s); /* 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]; if (!noWait) waitForVideoSync (s); if ((mask & COMP_SCREEN_DAMAGE_ALL_MASK) || (alwaysSwap == TRUE)) { 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; }