/** * * Compiz group plugin * * tab.c * * Copyright : (C) 2006-2007 by Patrick Niklaus, Roi Cohen, Danny Baumann * Authors: Patrick Niklaus * Roi Cohen * Danny Baumann * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * **/ #include "group-internal.h" /* * groupGetCurrentMousePosition * * Description: * Return the current function of the pointer at the given screen. * The position is queried trough XQueryPointer directly from the xserver. * */ Bool groupGetCurrentMousePosition(CompScreen *s, int *x, int *y) { unsigned int rmask; int mouseX, mouseY, winX, winY; Window root; Window child; Bool result; result = XQueryPointer (s->display->display, s->root, &root, &child, &mouseX, &mouseY, &winX, &winY, &rmask); if (result) { (*x) = mouseX; (*y) = mouseY; } return result; } /* * groupGetClippingRegion * * Description: * This function returns a clipping region which is used to clip * several events involving window stack such as hover detection * in the tab bar or Drag'n'Drop. It creates the clipping region * with getting the region of every window above the given window * and then adds this region to the clipping region using * XUnionRectWithRegion. w->region won't work since it doesn't include * the window decoration. * */ Region groupGetClippingRegion(CompWindow *w) { Region clip = XCreateRegion(); CompWindow *cw; for(cw = w->next; cw; cw = cw->next) { if (!cw->invisible && !(cw->state & CompWindowStateHiddenMask)) { Region buf = XCreateRegion(); XRectangle rect; rect.x = WIN_REAL_X(cw); rect.y = WIN_REAL_Y(cw); rect.width = WIN_REAL_WIDTH(cw); rect.height = WIN_REAL_HEIGHT(cw); XUnionRectWithRegion(&rect, buf, buf); XUnionRegion(clip, buf, clip); XDestroyRegion(buf); } } return clip; } /* * groupClearWindowInputShape * */ void groupClearWindowInputShape(CompWindow *w, GroupWindowHideInfo *hideInfo) { XRectangle *rects; int count, ordering; rects = XShapeGetRectangles(w->screen->display->display, w->id, ShapeInput, &count, &ordering); if (count == 0) { XFree(rects); return; } /* check if the returned shape exactly matches the window shape - if that is true, the window currently has no set input shape */ if ((count == 1) && (rects[0].x == -w->serverBorderWidth) && (rects[0].y == -w->serverBorderWidth) && (rects[0].width == (w->serverWidth + w->serverBorderWidth)) && (rects[0].height == (w->serverHeight + w->serverBorderWidth))) { count = 0; } if (hideInfo->inputRects) XFree(hideInfo->inputRects); hideInfo->inputRects = rects; hideInfo->nInputRects = count; hideInfo->inputRectOrdering = ordering; XShapeSelectInput(w->screen->display->display, w->id, NoEventMask); XShapeCombineRectangles(w->screen->display->display, w->id, ShapeInput, 0, 0, NULL, 0, ShapeSet, 0); XShapeSelectInput(w->screen->display->display, w->id, ShapeNotify); } /* * groupSetWindowVisibility * */ void groupSetWindowVisibility(CompWindow *w, Bool visible) { GROUP_WINDOW(w); if (!visible && !gw->windowHideInfo) { gw->windowHideInfo = malloc(sizeof(GroupWindowHideInfo)); if (!gw->windowHideInfo) return; gw->windowHideInfo->inputRects = NULL; gw->windowHideInfo->nInputRects = 0; gw->windowHideInfo->shapeMask = XShapeInputSelected( w->screen->display->display, w->id); groupClearWindowInputShape(w, gw->windowHideInfo); if (w->frame) { gw->windowHideInfo->frameWindow = w->frame; XUnmapWindow(w->screen->display->display, w->frame); } else gw->windowHideInfo->frameWindow = None; gw->windowHideInfo->skipState = w->state & (CompWindowStateSkipPagerMask | CompWindowStateSkipTaskbarMask); changeWindowState(w, w->state | CompWindowStateSkipPagerMask | CompWindowStateSkipTaskbarMask); } else if (visible && gw->windowHideInfo) { if (gw->windowHideInfo->nInputRects) { XShapeCombineRectangles(w->screen->display->display, w->id, ShapeInput, 0, 0, gw->windowHideInfo->inputRects, gw->windowHideInfo->nInputRects, ShapeSet, gw->windowHideInfo->inputRectOrdering); } else { XShapeCombineMask(w->screen->display->display, w->id, ShapeInput, 0, 0, None, ShapeSet); } if (gw->windowHideInfo->inputRects) XFree(gw->windowHideInfo->inputRects); XShapeSelectInput(w->screen->display->display, w->id, gw->windowHideInfo->shapeMask); if (gw->windowHideInfo->frameWindow) { if (w->attrib.map_state != IsUnmapped) XMapWindow(w->screen->display->display, w->frame); } changeWindowState(w, (w->state & ~(CompWindowStateSkipPagerMask | CompWindowStateSkipTaskbarMask)) | gw->windowHideInfo->skipState); free(gw->windowHideInfo); gw->windowHideInfo = NULL; } } /* * groupTabBarTimeout * * Description: * This function is called when the time expired (== timeout). * We use this to realize a delay with the bar hiding after tab change. * groupHandleAnimation sets up a timer after the animation has finished. * This function itself basically just sets the tab bar to a PaintOff status * through calling groupSetTabBarVisibility. The PERMANENT mask allows you to fore * a hiding of even PaintPermanentOn tab bars. * */ static Bool groupTabBarTimeout(void *data) { GroupSelection *group = (GroupSelection *) data; groupTabSetVisibility(group, FALSE, PERMANENT); group->tabBar->timeoutHandle = 0; return FALSE; //This will free the timer. } /* * groupShowDelayTimeout * */ static Bool groupShowDelayTimeout(void *data) { int mouseX, mouseY; GroupSelection *group = (GroupSelection *) data; CompScreen *s = group->screen; GROUP_SCREEN(s); if (!HAS_TOP_WIN(group)) { gs->showDelayTimeoutHandle = 0; return FALSE; //This will free the timer. } CompWindow *topTab = group->topTab->window; groupGetCurrentMousePosition(s, &mouseX, &mouseY); groupRecalcTabBarPos(group, mouseX, WIN_REAL_X(topTab), WIN_REAL_X(topTab) + WIN_REAL_WIDTH(topTab)); groupTabSetVisibility(group, TRUE, 0); gs->showDelayTimeoutHandle = 0; return FALSE; //This will free the timer. } /* * groupCheckForVisibleTabBars * * Description: * This function is used to update the gs->tabBarVisible * attribute which is used in groupPaintOutput to do add * a PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS_MAS mask to the * screen mask. It checks if there are any tab bar's with * a PaintState which is no PaintOff on the given screen. * */ void groupCheckForVisibleTabBars(CompScreen *s) { GroupSelection *group; GROUP_SCREEN(s); gs->tabBarVisible = FALSE; for (group = gs->groups; group; group = group->next) { if (group->tabBar && (group->tabBar->state != PaintOff)) { gs->tabBarVisible = TRUE; break; } } } /* * groupTabSetVisibility * * Description: * This function is used to set the visibility of the tab bar. * The "visibility" is indicated through the PaintState, which * can be PaintOn, PaintOff, PaintFadeIn, PaintFadeOut * and PaintPermantOn. * Currently the mask paramater is mostely used for the PERMANENT mask. * This mask affects how the visible parameter is handled, for example if * visibule is set to TRUE and the mask to PERMANENT state it will set * PaintPermanentOn state for the tab bar. When visibile is FALSE, mask 0 * and the current state of the tab bar is PaintPermanentOn it won't do anything * because its not strong enough to disable a Permanent-State, for those you need * the mask. * */ void groupTabSetVisibility(GroupSelection *group, Bool visible, unsigned int mask) { // fixes bad crash... if (!group || !group->windows || !group->tabBar || !HAS_TOP_WIN(group)) return; GroupTabBar *bar = group->tabBar; CompWindow *topTab = TOP_TAB(group); PaintState oldState; oldState = bar->state; /* hide tab bars for invisible top windows */ if ((topTab->state & CompWindowStateHiddenMask) || topTab->invisible) { bar->state = PaintOff; groupSwitchTopTabInput(group, TRUE); } else if (visible && bar->state != PaintPermanentOn && (mask & PERMANENT)) { bar->state = PaintPermanentOn; groupSwitchTopTabInput(group, FALSE); } else if (visible && bar->state == PaintPermanentOn && !(mask & PERMANENT)) { bar->state = PaintOn; } else if (visible && (bar->state == PaintOff || bar->state == PaintFadeOut)) { if (groupGetBarAnimations(group->screen)) { bar->bgAnimation = AnimationReflex; bar->bgAnimationTime = groupGetReflexTime(group->screen) * 1000.0; } bar->state = PaintFadeIn; groupSwitchTopTabInput(group, FALSE); } else if (!visible && (bar->state != PaintPermanentOn || (mask & PERMANENT)) && (bar->state == PaintOn || bar->state == PaintPermanentOn || bar->state == PaintFadeIn)) { bar->state = PaintFadeOut; groupSwitchTopTabInput(group, TRUE); } if (bar->state != oldState && bar->state != PaintPermanentOn) // FIXME remove that when we have a new state for PaintPermanentFadeIn bar->animationTime = (groupGetFadeTime(group->screen) * 1000) - bar->animationTime; if (bar->state != oldState) groupDamageTabBarRegion (group); groupCheckForVisibleTabBars(group->screen); } /* * groupGetDrawOffsetForSlot * * Description: * Its used when the draggedSlot is dragged to another viewport. * It calculates a correct offset to the real slot position. * */ void groupGetDrawOffsetForSlot(GroupTabBarSlot *slot, int *hoffset, int *voffset) { if (!slot || !slot->window) return; CompWindow *w = slot->window; CompWindow *topTab; GROUP_WINDOW(w); GROUP_SCREEN(w->screen); if (slot != gs->draggedSlot) { if (hoffset) *hoffset = 0; if (voffset) *voffset = 0; return; } int vx, vy; int oldX = w->serverX; int oldY = w->serverY; if (HAS_TOP_WIN(gw->group)) topTab = TOP_TAB(gw->group); else if (HAS_PREV_TOP_WIN(gw->group)) topTab = PREV_TOP_TAB(gw->group); else { if (hoffset) *hoffset = 0; if (voffset) *voffset = 0; return; } if (gw->group) { w->serverX = WIN_X(topTab) + WIN_WIDTH(topTab) / 2 - WIN_WIDTH(w) / 2; w->serverY = WIN_Y(topTab) + WIN_HEIGHT(topTab) / 2 - WIN_HEIGHT(w) / 2; } defaultViewportForWindow(w, &vx, &vy); if (hoffset) *hoffset = ((w->screen->x - vx) % w->screen->hsize) * w->screen->width; if (voffset) *voffset = ((w->screen->y - vy) % w->screen->vsize) * w->screen->height; w->serverX = oldX; w->serverY = oldY; } /* * groupHandleScreenActions * * Description: * This function handles some actions, which need to be applied * on all windows but only once. This is needed for some stuff, * which needs to be called on screen initial, but can't be called * inside initScreen because it uses a WRAPED function. * It's called from groupPreparePaintScreen. * */ void groupHandleScreenActions(CompScreen *s) { GROUP_SCREEN(s); if (!gs->screenActions) return; CompWindow *w; // we need to do it from top to buttom of the stack to avoid problems // with a reload of beryl and tabbed static groups. (topTab will always // be above the other windows in the group) for(w = s->reverseWindows; w; w = w->prev) { GROUP_WINDOW(w); if (gs->screenActions & CHECK_WINDOW_PROPERTIES) { Bool tabbed; long int id; GLushort color[3]; /* read window property to see if window was grouped before - if it was, regroup */ if (groupCheckWindowProperty(w, &id, &tabbed, color)) { GroupSelection *group = groupFindGroupByID(w->screen, id); groupAddWindowToGroup(w, group, id); if (tabbed) groupTabGroup(w); gw->group->color[0] = color[0]; gw->group->color[1] = color[1]; gw->group->color[2] = color[2]; groupRenderTopTabHighlight(gw->group); damageScreen(w->screen); } } if (gs->screenActions & APPLY_AUTO_TABBING) { if (groupGetAutotabCreate(s) && !w->invisible && matchEval(groupGetWindowMatch(s), w)) { if (!gw->group && (gw->windowState == WindowNormal)) { groupAddWindowToGroup(w, NULL, 0); groupTabGroup(w); } } } } // clear up the pending actions if (gs->screenActions & CHECK_WINDOW_PROPERTIES) gs->screenActions &= ~CHECK_WINDOW_PROPERTIES; if (gs->screenActions & APPLY_AUTO_TABBING) gs->screenActions &= ~APPLY_AUTO_TABBING; } /* * groupHandleHoverDetection * * Description: * This function is called from groupPreparePaintScreen to handle * the hover detection. This is needed for the text showing up, * when you hover a thumb on the thumb bar. * */ void groupHandleHoverDetection(GroupSelection *group) { GroupTabBar *bar = group->tabBar; if (!HAS_TOP_WIN(group)) return; CompWindow *topTab = TOP_TAB(group); if (bar->state != PaintOff) { // Tab-bar is visible. int mouseX, mouseY; Bool mouseOnScreen; // first get the current mouse position mouseOnScreen = groupGetCurrentMousePosition(group->screen, &mouseX, &mouseY); // then check if the mouse is in the last hovered slot -- this saves a lot of CPU usage if (mouseOnScreen && !(bar->hoveredSlot && XPointInRegion(bar->hoveredSlot->region, mouseX, mouseY))) { bar->hoveredSlot = NULL; Region clip; clip = groupGetClippingRegion(topTab); GroupTabBarSlot *slot; for (slot = bar->slots; slot; slot = slot->next) { // We need to clip the slot region with the clip region first. // This is needed to respect the window stack, so if a window // covers a port of that slot, this part won't be used for in-slot-detection. Region reg = XCreateRegion(); XSubtractRegion(slot->region, clip, reg); if (XPointInRegion(reg, mouseX, mouseY)) { bar->hoveredSlot = slot; XDestroyRegion(reg); break; } XDestroyRegion(reg); } XDestroyRegion(clip); if (bar->textLayer) { // trigger a FadeOut of the text if ((bar->hoveredSlot != bar->textSlot) && (bar->textLayer->state == PaintFadeIn || bar->textLayer->state == PaintOn)) { bar->textLayer->animationTime = (groupGetFadeTextTime(group->screen) * 1000) - bar->textLayer->animationTime; bar->textLayer->state = PaintFadeOut; } // or trigger a FadeIn of the text else if (bar->textLayer->state == PaintFadeOut && bar->hoveredSlot == bar->textSlot && bar->hoveredSlot) { bar->textLayer->animationTime = (groupGetFadeTextTime(group->screen) * 1000) - bar->textLayer->animationTime; bar->textLayer->state = PaintFadeIn; } } } } } /* * groupHandleTabBarFade * * Description: * This function is called from groupPreparePaintScreen * to handle the tab bar fade. It checks the animationTime and updates it, * so we can calculate the alpha of the tab bar in the painting code with it. * */ void groupHandleTabBarFade(GroupSelection *group, int msSinceLastPaint) { GroupTabBar *bar = group->tabBar; if ((bar->state == PaintFadeIn || bar->state == PaintFadeOut) && bar->animationTime > 0) { bar->animationTime -= msSinceLastPaint; if (bar->animationTime < 0) bar->animationTime = 0; // Fade finished if (bar->animationTime == 0) { if (bar->state == PaintFadeIn) { bar->state = PaintOn; groupCheckForVisibleTabBars(group->screen); } else if (bar->state == PaintFadeOut) { bar->state = PaintOff; groupCheckForVisibleTabBars(group->screen); if (bar->textLayer) { // Tab-bar is no longer painted, clean up text animation variables. bar->textLayer->animationTime = 0; bar->textLayer->state = PaintOff; bar->textSlot = bar->hoveredSlot = NULL; groupRenderWindowTitle(group); } } } } } /* * groupHandleTextFade * * Description: * This function is called from groupPreparePaintScreen * to handle the text fade. It checks the animationTime and updates it, * so we can calculate the alpha of the text in the painting code with it. * */ void groupHandleTextFade(GroupSelection *group, int msSinceLastPaint) { GroupTabBar *bar = group->tabBar; GroupCairoLayer *textLayer = bar->textLayer; if (!textLayer) return; // Fade in progress... if ((textLayer->state == PaintFadeIn || textLayer->state == PaintFadeOut) && textLayer->animationTime > 0) { textLayer->animationTime -= msSinceLastPaint; if (textLayer->animationTime < 0) textLayer->animationTime = 0; // Fade has finished. if (textLayer->animationTime == 0) { if (textLayer->state == PaintFadeIn) textLayer->state = PaintOn; else if (textLayer->state == PaintFadeOut) textLayer->state = PaintOff; } } if (textLayer->state == PaintOff && bar->hoveredSlot) { // Start text animation for the new hovered slot. bar->textSlot = bar->hoveredSlot; textLayer->state = PaintFadeIn; textLayer->animationTime = (groupGetFadeTextTime(group->screen) * 1000); groupRenderWindowTitle(group); } else if (textLayer->state == PaintOff && bar->textSlot) { // Clean Up. bar->textSlot = NULL; groupRenderWindowTitle(group); } } /* * groupHanldeTabBarAnimation * * Description: Handles the different animations for the tab bar defined in * GroupAnimationType. Basically that means this function updates * tabBar->animation->time as well as checking if the animation is already * finished. * */ void groupHandleTabBarAnimation(GroupSelection *group, int msSinceLastPaint) { GroupTabBar *bar = group->tabBar; if (bar->bgAnimation) { bar->bgAnimationTime -= msSinceLastPaint; if (bar->bgAnimationTime <= 0) { bar->bgAnimationTime = 0; bar->bgAnimation = 0; groupRenderTabBarBackground(group); } } } /* * groupTabChangeActivateEvent * * Description: Creates a compiz event to let other plugins know about * the starting and ending point of the tab changing animation */ static void groupTabChangeActivateEvent (CompScreen *s, Bool activating) { CompOption o[2]; o[0].type = CompOptionTypeInt; o[0].name = "root"; o[0].value.i = s->root; o[1].type = CompOptionTypeBool; o[1].name = "active"; o[1].value.b = activating; (*s->display->handleCompizEvent) (s->display, "group", "tabChangeActivate", o, 2); } /* * groupHandleTabChange * * Description: * This function is also called from groupHandleChanges to handle * the tab change. It moved the new topTab on the screen as well as doing * the initial set for the tab change animation. * */ static void groupHandleTabChange(CompScreen *s, GroupSelection *group) { GROUP_SCREEN(s); if (!group || !HAS_TOP_WIN(group) || !group->changeTab) return; // exit when there is a rotate or plane animation if (screenGrabExist(s, "rotate", "plane", 0)) return; CompWindow* topTab = TOP_TAB(group); if(group->tabbingState != PaintOff) { // if the previous top-tab window is being removed from the group, move the new top-tab window onscreen. if(group->ungroupState == UngroupSingle && group->prevTopTab == NULL) { gs->queued = TRUE; groupSetWindowVisibility(topTab, TRUE); moveWindow(topTab, group->oldTopTabCenterX - WIN_X(topTab) - WIN_WIDTH(topTab) / 2, group->oldTopTabCenterY - WIN_Y(topTab) - WIN_HEIGHT(topTab) / 2, TRUE, TRUE); syncWindowPosition(topTab); gs->queued = FALSE; // recalc here is needed (for y value)! groupRecalcTabBarPos(group, (group->tabBar->region->extents.x1 + group->tabBar->region->extents.x2) / 2, WIN_REAL_X(topTab), WIN_REAL_X(topTab) + WIN_REAL_WIDTH(topTab)); group->prevTopTab = group->topTab; } return; } gs->queued = TRUE; groupSetWindowVisibility(topTab, TRUE); moveWindow(topTab, group->oldTopTabCenterX - WIN_X(topTab) - WIN_WIDTH(topTab) / 2, group->oldTopTabCenterY - WIN_Y(topTab) - WIN_HEIGHT(topTab) / 2, TRUE, TRUE); syncWindowPosition(topTab); gs->queued = FALSE; if(group->prevTopTab) { //we use only the half time here - the second half will be PaintFadeOut group->changeAnimationTime = groupGetChangeAnimationTime(s) * 500; groupTabChangeActivateEvent(s, TRUE); group->changeState = PaintFadeIn; group->changeTab = FALSE; } else //No window to do animation with. { group->prevTopTab = group->topTab; group->changeTab = FALSE; activateWindow (TOP_TAB(group)); } return; } /* * groupHandleAnimation * * Description: * This function handles the change animation. It's called * from groupHandleChanges. Don't let the changeState * confuse you, PaintFadeIn equals with the start of the * rotate animation and PaintFadeOut is the end of these * animation. * */ static void groupHandleAnimation(CompScreen *s, GroupSelection *group) { if(group->tabbingState != PaintOff || !HAS_TOP_WIN(group)) return; if (screenGrabExist(s, "rotate", "plane", 0)) return; if(group->changeState == PaintFadeIn && group->changeAnimationTime <= 0) { // recalc here is needed (for y value)! groupRecalcTabBarPos(group, (group->tabBar->region->extents.x1 + group->tabBar->region->extents.x2) / 2, WIN_REAL_X(TOP_TAB(group)), WIN_REAL_X(TOP_TAB(group)) + WIN_REAL_WIDTH(TOP_TAB(group))); group->changeAnimationTime += groupGetChangeAnimationTime(s) * 500; if (group->changeAnimationTime <= 0) group->changeAnimationTime = 0; group->changeState = PaintFadeOut; if (HAS_TOP_WIN(group)) activateWindow (TOP_TAB(group)); } if (group->changeState == PaintFadeOut && group->changeAnimationTime <= 0) { groupTabChangeActivateEvent(s, FALSE); int oldChangeAnimationTime = group->changeAnimationTime; if (group->prevTopTab) groupSetWindowVisibility(PREV_TOP_TAB(group), FALSE); group->prevTopTab = group->topTab; group->changeState = PaintOff; if (group->nextTopTab) { groupChangeTab(group->nextTopTab, group->nextDirection); group->nextTopTab = NULL; groupHandleTabChange(s, group); if (group->changeState == PaintFadeIn) group->changeAnimationTime += oldChangeAnimationTime; // If a new animation was started. } if (group->changeAnimationTime <= 0) group->changeAnimationTime = 0; else if (groupGetVisibilityTime(s) != 0.0f && group->changeState == PaintOff) { groupTabSetVisibility (group, TRUE, PERMANENT | SHOW_BAR_INSTANTLY_MASK); if (group->tabBar->timeoutHandle) compRemoveTimeout(group->tabBar->timeoutHandle); group->tabBar->timeoutHandle = compAddTimeout ( groupGetVisibilityTime(s) * 1000, groupTabBarTimeout, group); } } } /* * groupHandleTab * * Description: * This functions handes the offscreen moves of the tabs * after the animation has finished. It's called from * groupHandleChanges. * */ static void groupHandleTab(CompScreen *s, GroupSelection *group) { if (group->tabbingState == PaintOff || group->doTabbing || !HAS_TOP_WIN(group) || !group->changeTab) return; GroupTabBarSlot *slot; for(slot = group->tabBar->slots; slot; slot = slot->next) { if (!slot->window) continue; CompWindow *w = slot->window; GROUP_WINDOW(w); if (slot == group->topTab || !(gw->animateState & FINISHED_ANIMATION) || gw->ungroup) continue; groupSetWindowVisibility(w, FALSE); } group->changeTab = FALSE; group->prevTopTab = group->topTab; return; } /* * groupHandleTabbingAnimation * * Description: * This function handles the end of the tab * animation. Actually its just sets some * states and sync the window positions with X. * It's called from groupHandleChanges. * */ static void groupHandleTabbingAnimation(CompScreen *s, GroupSelection *group) { int i; if (group->tabbingState == PaintOff || group->doTabbing) return; // Not animated any more. group->tabbingState = PaintOff; groupSyncWindows(group); for(i = 0; i < group->nWins; i++) { CompWindow *w = group->windows[i]; GROUP_WINDOW(w); gw->animateState = 0; } return; } /* * groupHandleUntab * * Description: * This function handles the beginning of the untab * animation. It deletes the tab bar and set's * group->prevTopTab. It's called from groupHandleChanges. * */ static void groupHandleUntab(CompScreen *s, GroupSelection *group) { if (group->tabbingState == PaintOff || !group->doTabbing) return; if (group->topTab || !group->changeTab) return; groupDeleteTabBar(group); group->changeAnimationTime = 0; group->changeState = PaintOff; group->nextTopTab = NULL; group->changeTab = FALSE; group->prevTopTab = group->topTab; return; } /* * groupHandleUngroup * * Description: * This function handles the ungroup animation for tabbed groups. * It moved the windows on screen and also it calles groupDeleteGroupWindow * when its a "single ungroup", which means only one window gets removed * from the group. Another task of this function is to check if the group * which is before the given group in the linked list needs to be deleted. * This is needed to avoid problems with the linked list. * It's called from groupHandleChanges. * */ static Bool groupHandleUngroup(CompScreen *s, GroupSelection *group) { int i; GROUP_SCREEN(s); if((group->ungroupState == UngroupSingle) && group->doTabbing && group->changeTab) { for(i = 0; i < group->nWins; i++) { CompWindow *w = group->windows[i]; GROUP_WINDOW(w); if (gw->ungroup) { gs->queued = TRUE; groupSetWindowVisibility(w, TRUE); moveWindow(w, group->oldTopTabCenterX - WIN_X(w) - WIN_WIDTH(w) / 2, group->oldTopTabCenterY - WIN_Y(w) - WIN_HEIGHT(w) / 2, TRUE, TRUE); syncWindowPosition(w); gs->queued = FALSE; } } group->changeTab = FALSE; } if ((group->ungroupState == UngroupSingle) && !group->doTabbing) { Bool morePending; do { morePending = FALSE; for(i = 0;i < group->nWins; i++) { CompWindow *w = group->windows[i]; GROUP_WINDOW(w); if (gw->ungroup) { groupDeleteGroupWindow(w, TRUE); gw->ungroup = FALSE; morePending = TRUE; } } } while (morePending); group->ungroupState = UngroupNone; } if (group->prev) { if ((group->prev->ungroupState == UngroupAll) && !group->prev->doTabbing) groupDeleteGroup(group->prev); } if (!group->next) { if ((group->ungroupState == UngroupAll) && !group->doTabbing) { groupDeleteGroup(group); return FALSE; } } return TRUE; } /* * groupHandleChanges * * Description: * This function is called from groupPreparePaintScreen to * go through all groups and apply the other "handle functions" * on them. * */ void groupHandleChanges(CompScreen* s) { GROUP_SCREEN(s); GroupSelection *group; for(group = gs->groups; group; group = group ? group->next : NULL) { groupHandleUntab(s, group); groupHandleTab(s, group); groupHandleTabbingAnimation(s, group); groupHandleTabChange(s, group); groupHandleAnimation(s, group); if (!groupHandleUngroup(s, group)) group = NULL; } } /* adjust velocity for each animation step (adapted from the scale plugin) */ static int adjustTabVelocity(CompWindow * w) { float dx, dy, adjust, amount; float x1, y1; GROUP_WINDOW(w); x1 = y1 = 0.0; if (!(gw->animateState & IS_ANIMATED)) return 0; x1 = gw->destination.x; y1 = gw->destination.y; dx = x1 - (w->serverX + gw->tx); adjust = dx * 0.15f; amount = fabs(dx) * 1.5f; if (amount < 0.5f) amount = 0.5f; else if (amount > 5.0f) amount = 5.0f; gw->xVelocity = (amount * gw->xVelocity + adjust) / (amount + 1.0f); dy = y1 - (w->serverY + gw->ty); adjust = dy * 0.15f; amount = fabs(dy) * 1.5f; if (amount < 0.5f) amount = 0.5f; else if (amount > 5.0f) amount = 5.0f; gw->yVelocity = (amount * gw->yVelocity + adjust) / (amount + 1.0f); if (fabs(dx) < 0.1f && fabs(gw->xVelocity) < 0.2f && fabs(dy) < 0.1f && fabs(gw->yVelocity) < 0.2f) { gw->xVelocity = gw->yVelocity = 0.0f; gw->tx = x1 - w->serverX; gw->ty = y1 - w->serverY; return 0; } return 1; } /* * groupDrawTabAnimation * * Description: * This function is called from groupPreparePaintScreen, to move * all the animated windows, with the required animation step. * The function goes through all grouped animated windows, calculates * the required step using adjustTabVelocity, moves the window, * and then checks if the animation is finished for that window. * */ void groupDrawTabAnimation(CompScreen * s, int msSinceLastPaint) { GROUP_SCREEN(s); int i; GroupSelection *group; for(group = gs->groups; group; group = group->next) { if(group->tabbingState == PaintOff) continue; int steps, dx, dy; float amount, chunk; amount = msSinceLastPaint * 0.05f * groupGetTabbingSpeed(s); steps = amount / (0.5f * groupGetTabbingTimestep(s)); if (!steps) steps = 1; chunk = amount / (float)steps; while (steps--) { group->doTabbing = FALSE; for(i = 0; i < group->nWins; i++) { CompWindow *cw = group->windows[i]; if(!cw) continue; GROUP_WINDOW(cw); if (!(gw->animateState & IS_ANIMATED)) continue; if (!adjustTabVelocity(cw)) { gw->animateState |= FINISHED_ANIMATION; gw->animateState &= ~(IS_ANIMATED); } gw->tx += gw->xVelocity * chunk; gw->ty += gw->yVelocity * chunk; dx = (cw->serverX + gw->tx) - cw->attrib.x; dy = (cw->serverY + gw->ty) - cw->attrib.y; group->doTabbing |= (gw->animateState & IS_ANIMATED); gs->queued = TRUE; moveWindow(cw, dx, dy, FALSE, FALSE); gs->queued = FALSE; } if (!group->doTabbing) break; } } } /* * groupUpdateTabBars * * Description: * This function is responsible for showing / unshowing the tab-bars, * when the title-bars / tab-bars are hovered. * The function is called whenever a new window is entered, * checks if the entered window is a window frame (and if the title * bar part of that frame was hovered) or if it was the input * prevention window of a tab bar, and sets tab-bar visibility * according to that. * */ void groupUpdateTabBars(CompScreen *s, Window enteredWin) { GROUP_SCREEN(s); CompWindow *w; GroupSelection *hoveredGroup = NULL; int mouseX = -1, mouseY; /* first check if the entered window is a frame */ for (w = s->windows; w; w = w->next) { if (w->frame == enteredWin) break; } if (w) { /* is the window the entered frame belongs to inside a tabbed group? if no, it's not interesting for us */ GROUP_WINDOW(w); if (gw->group && gw->group->tabBar) { /* it is grouped and tabbed, so now we have to check if we hovered the title bar or the frame */ if (groupGetCurrentMousePosition(s, &mouseX, &mouseY)) { Region reg = XCreateRegion(); XRectangle rect; rect.x = WIN_X(w) - w->input.left; rect.y = WIN_Y(w) - w->input.top; rect.width = WIN_WIDTH(w) + w->input.right; rect.height = WIN_Y(w) - rect.y; XUnionRectWithRegion(&rect, reg, reg); if (XPointInRegion(reg, mouseX, mouseY)) hoveredGroup = gw->group; XDestroyRegion(reg); } } } /* if we didn't hover a title bar, check if we hovered a tab bar (means: input prevention window) */ if (!hoveredGroup) { GroupSelection *group; for (group = gs->groups; group; group = group->next) { if (group->inputPrevention == enteredWin) { /* only accept it if the IPW is mapped */ if (group->ipwMapped) { hoveredGroup = group; break; } } } } /* if we found a hovered a tab bar different than the last one (or left a tab bar), hide the old one */ if (gs->lastHoveredGroup && (hoveredGroup != gs->lastHoveredGroup)) groupTabSetVisibility (gs->lastHoveredGroup, FALSE, 0); /* if we entered a tab bar (or title bar), show the tab bar */ if (hoveredGroup && HAS_TOP_WIN(hoveredGroup) && !TOP_TAB(hoveredGroup)->grabbed) { GroupTabBar *bar = hoveredGroup->tabBar; if (bar && ((bar->state == PaintOff) || (bar->state == PaintFadeOut))) { int showDelayTime = groupGetTabbarShowDelay(s) * 1000; /* Show the tab-bar after a delay, only if the tab-bar wasn't fading out. */ if (showDelayTime > 0 && (bar->state == PaintOff)) { if (gs->showDelayTimeoutHandle) compRemoveTimeout (gs->showDelayTimeoutHandle); gs->showDelayTimeoutHandle = compAddTimeout(showDelayTime, groupShowDelayTimeout, hoveredGroup); } else groupShowDelayTimeout(hoveredGroup); } } gs->lastHoveredGroup = hoveredGroup; } /* * groupGetConstrainRegion * */ static Region groupGetConstrainRegion(CompScreen *s) { CompWindow *w; Region region; REGION r; int i; region = XCreateRegion (); if (!region) return NULL; for (i = 0;i < s->nOutputDev; i++) XUnionRegion (&s->outputDev[i].region, region, region); r.rects = &r.extents; r.numRects = r.size = 1; for (w = s->windows; w; w = w->next) { if (!w->mapNum) continue; if (w->struts) { r.extents.x1 = w->struts->top.x; r.extents.y1 = w->struts->top.y; r.extents.x2 = r.extents.x1 + w->struts->top.width; r.extents.y2 = r.extents.y1 + w->struts->top.height; XSubtractRegion (region, &r, region); r.extents.x1 = w->struts->bottom.x; r.extents.y1 = w->struts->bottom.y; r.extents.x2 = r.extents.x1 + w->struts->bottom.width; r.extents.y2 = r.extents.y1 + w->struts->bottom.height; XSubtractRegion (region, &r, region); r.extents.x1 = w->struts->left.x; r.extents.y1 = w->struts->left.y; r.extents.x2 = r.extents.x1 + w->struts->left.width; r.extents.y2 = r.extents.y1 + w->struts->left.height; XSubtractRegion (region, &r, region); r.extents.x1 = w->struts->right.x; r.extents.y1 = w->struts->right.y; r.extents.x2 = r.extents.x1 + w->struts->right.width; r.extents.y2 = r.extents.y1 + w->struts->right.height; XSubtractRegion (region, &r, region); } } return region; } /* * groupConstrainMovement * */ static Bool groupConstrainMovement(CompWindow *w, Region constrainRegion, int dx, int dy, int *new_dx, int *new_dy) { GROUP_WINDOW(w); int status; int origDx = dx, origDy = dy; int x, y, width, height; if (!gw->group) return FALSE; if (!dx && !dy) return FALSE; x = gw->orgPos.x - w->input.left + dx; y = gw->orgPos.y - w->input.top + dy; width = WIN_REAL_WIDTH(w); height = WIN_REAL_HEIGHT(w); status = XRectInRegion(constrainRegion, x, y, width, height); int xStatus = status; while (dx && (xStatus != RectangleIn)) { xStatus = XRectInRegion(constrainRegion, x, y - dy, width, height); if (xStatus != RectangleIn) dx += (dx < 0) ? 1 : -1; x = gw->orgPos.x - w->input.left + dx; } while (dy && (status != RectangleIn)) { status = XRectInRegion(constrainRegion, x, y, width, height); if (status != RectangleIn) dy += (dy < 0) ? 1 : -1; y = gw->orgPos.y - w->input.top + dy; } if (new_dx) *new_dx = dx; if (new_dy) *new_dy = dy; if ((dx != origDx) || (dy != origDy)) return TRUE; else return FALSE; } /* * groupApplyConstrainingToWindows * */ static void groupApplyConstrainingToWindows(GroupSelection *group, Region constrainRegion, Window constrainedWindow, int dx, int dy) { int i; CompWindow *w; if (!dx && !dy) return; for (i = 0; i < group->nWins; i++) { w = group->windows[i]; GROUP_WINDOW(w); /* ignore certain windows: we don't want to apply the constraining results on the constrained window itself, not do we want to change the target position of unamimated windows and of windows which already are constrained */ if (w->id == constrainedWindow) continue; if(!(gw->animateState & IS_ANIMATED)) continue; if (gw->animateState & DONT_CONSTRAIN) continue; if (!(gw->animateState & CONSTRAINED_X)) { gw->animateState |= IS_ANIMATED; /* applying the constraining result of another window might move the window offscreen, too, so check if this is not the case */ if (groupConstrainMovement(w, constrainRegion, dx, 0, &dx, NULL)) gw->animateState |= CONSTRAINED_X; gw->destination.x += dx; gw->orgPos.x += dx; } if (!(gw->animateState & CONSTRAINED_Y)) { gw->animateState |= IS_ANIMATED; /* analog to X case */ if (groupConstrainMovement(w, constrainRegion, 0, dy, NULL, &dy)) gw->animateState |= CONSTRAINED_Y; gw->destination.y += dy; gw->orgPos.y += dy; } } } /* * groupStartTabbingAnimation * */ void groupStartTabbingAnimation(GroupSelection *group, Bool tab) { if (!group || (group->tabbingState != PaintOff)) return; CompScreen *s = group->screen; int i; int dx, dy; int constrainStatus; group->doTabbing = TRUE; group->changeTab = TRUE; group->tabbingState = (tab) ? PaintFadeIn : PaintFadeOut; if (!tab) { /* we need to set up the X/Y constraining on untabbing */ Region constrainRegion = groupGetConstrainRegion(s); Bool constrainedWindows = TRUE; if (!constrainRegion) return; /* reset all flags */ for (i = 0; i < group->nWins; i++) { GROUP_WINDOW(group->windows[i]); gw->animateState &= ~(CONSTRAINED_X | CONSTRAINED_Y | DONT_CONSTRAIN); } /* as we apply the constraining in a flat loop, we may need to run multiple times through this loop until all constraining dependencies are met */ while (constrainedWindows) { constrainedWindows = FALSE; /* loop through all windows and try to constrain their animation path (going from gw->orgPos to gw->destination) to the active screen area */ for (i = 0; i < group->nWins; i++) { CompWindow *w = group->windows[i]; GROUP_WINDOW(w); /* ignore windows which aren't animated and/or already are at the edge of the screen area */ if (!(gw->animateState & IS_ANIMATED)) continue; if (gw->animateState & DONT_CONSTRAIN) continue; /* is the original position inside the screen area? */ constrainStatus = XRectInRegion(constrainRegion, gw->orgPos.x - w->input.left, gw->orgPos.y - w->input.top, WIN_REAL_WIDTH(w), WIN_REAL_HEIGHT(w)); /* constrain the movement */ if (groupConstrainMovement(w, constrainRegion, gw->destination.x - gw->orgPos.x, gw->destination.y - gw->orgPos.y, &dx, &dy)) { /* handle the case where the window is outside the screen area on its whole animation path */ if (constrainStatus != RectangleIn && !dx && !dy) { gw->animateState |= DONT_CONSTRAIN; gw->animateState |= CONSTRAINED_X | CONSTRAINED_Y; /* use the original position as last resort */ gw->destination.x = gw->mainTabOffset.x; gw->destination.y = gw->mainTabOffset.y; } else { /* if we found a valid target position, apply the change also to other windows to retain the distance between the windows */ groupApplyConstrainingToWindows(group, constrainRegion, w->id, dx - gw->destination.x + gw->orgPos.x, dy - gw->destination.y + gw->orgPos.y); /* if we hit constraints, adjust the mask and the target position accordingly */ if (dx != (gw->destination.x - gw->orgPos.x)) { gw->animateState |= CONSTRAINED_X; gw->destination.x = gw->orgPos.x + dx; } if (dy != (gw->destination.y - gw->orgPos.y)) { gw->animateState |= CONSTRAINED_Y; gw->destination.y = gw->orgPos.y + dy; } constrainedWindows = TRUE; } } } } XDestroyRegion(constrainRegion); } } /* * groupTabGroup * */ void groupTabGroup(CompWindow *main) { GROUP_WINDOW(main); GroupSelection *group = gw->group; if(!group || group->tabBar) return; if (!main->screen->display->shapeExtension) { compLogMessage (main->screen->display, "group", CompLogLevelError, "No X shape extension! Tabbing disabled."); return; } groupInitTabBar(group, main); groupCreateInputPreventionWindow(group); group->tabbingState = PaintOff; groupChangeTab(gw->slot, RotateUncertain); //Slot is initialized after groupInitTabBar(group); groupRecalcTabBarPos(gw->group, WIN_X(main) + WIN_WIDTH(main)/2, WIN_X(main), WIN_X(main) + WIN_WIDTH(main)); int width, height; width = group->tabBar->region->extents.x2 - group->tabBar->region->extents.x1; height = group->tabBar->region->extents.y2 - group->tabBar->region->extents.y1; group->tabBar->textLayer = groupCreateCairoLayer(main->screen, width, height); if (group->tabBar->textLayer) { group->tabBar->textLayer->state = PaintOff; group->tabBar->textLayer->animationTime = 0; groupRenderWindowTitle(group); group->tabBar->textLayer->animationTime = groupGetFadeTextTime(main->screen) * 1000; group->tabBar->textLayer->state = PaintFadeIn; } // we need a buffer for DnD here int space = groupGetThumbSpace(main->screen); int thumb_size = groupGetThumbSize(main->screen); group->tabBar->bgLayer = groupCreateCairoLayer(main->screen, width + space + thumb_size, height); if (group->tabBar->bgLayer) { group->tabBar->bgLayer->state = PaintOn; group->tabBar->bgLayer->animationTime = 0; groupRenderTabBarBackground(group); } width = group->topTab->region->extents.x2 - group->topTab->region->extents.x1; height = group->topTab->region->extents.y2 - group->topTab->region->extents.y1; group->tabBar->selectionLayer = groupCreateCairoLayer(main->screen, width, height); if (group->tabBar->selectionLayer) { group->tabBar->selectionLayer->state = PaintOn; group->tabBar->selectionLayer->animationTime = 0; groupRenderTopTabHighlight(group); } if(!HAS_TOP_WIN(group)) return; GroupTabBarSlot *slot; for(slot = group->tabBar->slots; slot; slot = slot->next) { CompWindow *cw = slot->window; GROUP_WINDOW(cw); int x = WIN_X(cw), y = WIN_Y(cw); if(gw->animateState & IS_ANIMATED) { x = gw->destination.x; y = gw->destination.y; } // center the window to the main window gw->destination.x = WIN_X(main) + (WIN_WIDTH(main) / 2) - (WIN_WIDTH(cw) / 2); gw->destination.y = WIN_Y(main) + (WIN_HEIGHT(main) / 2) - (WIN_HEIGHT(cw) / 2); gw->mainTabOffset.x = x - gw->destination.x; //Distance from destination. gw->mainTabOffset.y = y - gw->destination.y; gw->orgPos.x = WIN_X(cw); gw->orgPos.y = WIN_Y(cw); gw->tx = gw->ty = gw->xVelocity = gw->yVelocity = 0.0f; gw->animateState |= IS_ANIMATED; } groupStartTabbingAnimation(group, TRUE); } /* * groupUntabGroup * */ void groupUntabGroup(GroupSelection *group) { if(!HAS_TOP_WIN(group)) return; GROUP_WINDOW(TOP_TAB(group)); GROUP_SCREEN(TOP_TAB(group)->screen); int mainOrgPosX = gw->mainTabOffset.x; int mainOrgPosY = gw->mainTabOffset.y; int oldX, oldY; CompWindow* prevTopTab; if(group->prevTopTab) prevTopTab = PREV_TOP_TAB(group); else prevTopTab = TOP_TAB(group); //If prevTopTab isn't set, we have no choice but using topTab. //It happens when there is still animation, which means the tab wasn't changed anyway. group->oldTopTabCenterX = WIN_X(prevTopTab) + WIN_WIDTH(prevTopTab)/2; group->oldTopTabCenterY = WIN_Y(prevTopTab) + WIN_HEIGHT(prevTopTab)/2; group->topTab = NULL; GroupTabBarSlot *slot; for(slot = group->tabBar->slots; slot; slot = slot->next) { CompWindow *cw = slot->window; GROUP_WINDOW(cw); gs->queued = TRUE; groupSetWindowVisibility(cw, TRUE); moveWindow(cw, group->oldTopTabCenterX - WIN_X(cw) - WIN_WIDTH(cw) / 2, group->oldTopTabCenterY - WIN_Y(cw) - WIN_HEIGHT(cw) / 2, TRUE, TRUE); syncWindowPosition(cw); gs->queued = FALSE; /* save the old original position - we might need it if constraining fails */ oldX = gw->orgPos.x; oldY = gw->orgPos.y; gw->orgPos.x = group->oldTopTabCenterX - WIN_WIDTH(cw) / 2; gw->orgPos.y = group->oldTopTabCenterY - WIN_HEIGHT(cw) / 2; gw->destination.x = WIN_X(prevTopTab) + WIN_WIDTH(prevTopTab)/2 - WIN_WIDTH(cw)/2 + gw->mainTabOffset.x - mainOrgPosX; gw->destination.y = WIN_Y(prevTopTab) + WIN_HEIGHT(prevTopTab)/2 - WIN_HEIGHT(cw)/2 + gw->mainTabOffset.y - mainOrgPosY; gw->mainTabOffset.x = oldX; gw->mainTabOffset.y = oldY; gw->animateState |= IS_ANIMATED; gw->tx = gw->ty = gw->xVelocity = gw->yVelocity = 0.0f; } group->tabbingState = PaintOff; groupStartTabbingAnimation(group, FALSE); damageScreen(group->screen); } /* * groupChangeTab * */ Bool groupChangeTab(GroupTabBarSlot* topTab, ChangeTabAnimationDirection direction) { if (!topTab) return TRUE; CompWindow* w = topTab->window; GROUP_WINDOW(w); GroupSelection *group = gw->group; if (!group || group->tabbingState != PaintOff) return TRUE; if (group->changeState == PaintOff && group->topTab == topTab) return TRUE; if (group->changeState != PaintOff && group->nextTopTab == topTab) return TRUE; if (group->prevTopTab && group->changeState == PaintOff) { group->oldTopTabCenterX = WIN_X(PREV_TOP_TAB(group)) + WIN_WIDTH(PREV_TOP_TAB(group))/2; group->oldTopTabCenterY = WIN_Y(PREV_TOP_TAB(group)) + WIN_HEIGHT(PREV_TOP_TAB(group))/2; } if (group->changeState != PaintOff) group->nextDirection = direction; else if (direction == RotateLeft) group->changeAnimationDirection = 1; else if (direction == RotateRight) group->changeAnimationDirection = -1; else { int distanceOld = 0, distanceNew = 0; GroupTabBarSlot *slot; if (group->topTab) for (slot = group->tabBar->slots; slot && (slot != group->topTab); slot = slot->next, distanceOld++); for (slot = group->tabBar->slots; slot && (slot != topTab); slot = slot->next, distanceNew++); if (distanceNew < distanceOld) group->changeAnimationDirection = 1; //left else group->changeAnimationDirection = -1; //right //check if the opposite direction is shorter if (abs(distanceNew - distanceOld) > (group->tabBar->nSlots / 2)) group->changeAnimationDirection *= -1; } if (group->changeState != PaintOff) { if (group->prevTopTab == topTab) { // Reverse animation. GroupTabBarSlot *tmp = group->topTab; group->topTab = group->prevTopTab; group->prevTopTab = tmp; group->changeAnimationDirection *= -1; group->changeAnimationTime = groupGetChangeAnimationTime(w->screen) * 500 - group->changeAnimationTime; group->changeState = (group->changeState == PaintFadeIn)? PaintFadeOut: PaintFadeIn; group->nextTopTab = NULL; } else group->nextTopTab = topTab; } else { group->topTab = topTab; group->changeTab = (group->prevTopTab != topTab); groupRenderWindowTitle(group); groupRenderTopTabHighlight(group); addWindowDamage(w); } return TRUE; } /* * groupRebuildCairoLayer * */ GroupCairoLayer* groupRebuildCairoLayer(CompScreen *s, GroupCairoLayer *layer, int width, int height) { int timeBuf = layer->animationTime; PaintState stateBuf = layer->state; groupDestroyCairoLayer(s, layer); layer = groupCreateCairoLayer(s, width, height); if (!layer) return NULL; layer->animationTime = timeBuf; layer->state = stateBuf; return layer; } /* * groupClearCairoLayer * */ void groupClearCairoLayer(GroupCairoLayer *layer) { cairo_t *cr = layer->cairo; cairo_save(cr); cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); cairo_paint(cr); cairo_restore(cr); } /* * groupDestroyCairoLayer * */ void groupDestroyCairoLayer(CompScreen *s, GroupCairoLayer *layer) { if (layer->cairo) cairo_destroy(layer->cairo); if (layer->surface) cairo_surface_destroy(layer->surface); finiTexture(s, &layer->texture); if (layer->buffer) free(layer->buffer); free(layer); } /* * groupCreateCairoLayer * */ GroupCairoLayer* groupCreateCairoLayer(CompScreen *s, int width, int height) { GroupCairoLayer* layer = (GroupCairoLayer*) malloc(sizeof(GroupCairoLayer)); Screen *screen; Display *display; display = s->display->display; screen = ScreenOfDisplay (display, s->screenNum); layer->surface = NULL; layer->cairo = NULL; layer->buffer = NULL; layer->animationTime = 0; layer->state = PaintOff; layer->texWidth = width; layer->texHeight = height; initTexture(s, &layer->texture); layer->buffer = calloc(4 * width * height, sizeof(unsigned char)); if (!layer->buffer) { printf("ERROR: Failed to alloc buffer!\n"); groupDestroyCairoLayer(s, layer); return NULL; } layer->surface = cairo_image_surface_create_for_data(layer->buffer, CAIRO_FORMAT_ARGB32, width, height, 4*width); if (cairo_surface_status(layer->surface) != CAIRO_STATUS_SUCCESS) { printf("ERROR: Failed to create surface!\n"); groupDestroyCairoLayer(s, layer); return NULL; } layer->cairo = cairo_create(layer->surface); if (cairo_status(layer->cairo) != CAIRO_STATUS_SUCCESS) { printf("ERROR: Failed to create context!\n"); groupDestroyCairoLayer(s, layer); return NULL; } groupClearCairoLayer(layer); return layer; } /* * groupRecalcSlotPos * */ static void groupRecalcSlotPos(GroupTabBarSlot *slot, int slotPos) { GROUP_WINDOW (slot->window); GroupSelection *group = gw->group; XRectangle box; if (!HAS_TOP_WIN(group) || !group->tabBar) return; int space = groupGetThumbSpace(slot->window->screen); int thumb_size = groupGetThumbSize(slot->window->screen); EMPTY_REGION(slot->region); box.x = space + ((thumb_size + space) * slotPos); box.y = space; box.width = thumb_size; box.height = thumb_size; XUnionRectWithRegion(&box, slot->region, slot->region); } /* * groupRecalcTabBarPos * */ void groupRecalcTabBarPos(GroupSelection *group, int middleX, int minX1, int maxX2) { if (!HAS_TOP_WIN(group) || !group->tabBar) return; GroupTabBarSlot *slot; GroupTabBar *bar = group->tabBar; CompWindow *topTab = TOP_TAB(group); Bool isDraggedSlotGroup = FALSE; GROUP_SCREEN(group->screen); int space = groupGetThumbSpace(group->screen); int bar_width; int currentSlot; XRectangle box; // calculate the space which the tabs need int tabs_width = 0; int tabs_height = 0; for(slot = bar->slots; slot; slot = slot->next) { if (slot == gs->draggedSlot && gs->dragged) { isDraggedSlotGroup = TRUE; continue; } tabs_width += (slot->region->extents.x2 - slot->region->extents.x1); if ((slot->region->extents.y2 - slot->region->extents.y1) > tabs_height) tabs_height = slot->region->extents.y2 - slot->region->extents.y1; } // just a little work-a-round for first call int thumb_size = groupGetThumbSize(group->screen); if (bar->nSlots && tabs_width <= 0) { // first call tabs_width = thumb_size * bar->nSlots; if (bar->nSlots && tabs_height < thumb_size) // we need to do the standard height too tabs_height = thumb_size; if(isDraggedSlotGroup) tabs_width -= thumb_size; } bar_width = space * (bar->nSlots + 1) + tabs_width; if (isDraggedSlotGroup) bar_width -= space; //1 tab is missing, so we have 1 less border if (maxX2 - minX1 < bar_width) box.x = (maxX2 + minX1)/2 - bar_width / 2; else if (middleX - bar_width/2 < minX1) box.x = minX1; else if (middleX + bar_width/2 > maxX2) box.x = maxX2 - bar_width; else box.x = middleX - bar_width / 2; box.y = WIN_Y(topTab); box.width = bar_width; box.height = space * 2 + tabs_height; groupResizeTabBarRegion (group, &box, TRUE); // recalc every slot region currentSlot = 0; for(slot = bar->slots; slot; slot = slot->next) { if(slot == gs->draggedSlot && gs->dragged) continue; groupRecalcSlotPos (slot, currentSlot); XOffsetRegion (slot->region, bar->region->extents.x1, bar->region->extents.y1); slot->springX = (slot->region->extents.x1 + slot->region->extents.x2) / 2; slot->speed = 0; slot->msSinceLastMove = 0; currentSlot++; } bar->leftSpringX = box.x; bar->rightSpringX = box.x + box.width; bar->rightSpeed = 0; bar->leftSpeed = 0; bar->rightMsSinceLastMove = 0; bar->leftMsSinceLastMove = 0; } void groupDamageTabBarRegion (GroupSelection *group) { REGION reg; reg.rects = ®.extents; reg.numRects = 1; /* we use 15 pixels as damage buffer here, as there is a 10 pixel wide border around the selected slot which also needs to be damaged properly - however the best way would be if slot->region was sized including the border */ #define DAMAGE_BUFFER 20 reg.extents.x1 = MIN (group->tabBar->region->extents.x1, group->tabBar->slots->region->extents.x1) - DAMAGE_BUFFER; reg.extents.y1 = MIN (group->tabBar->region->extents.y1, group->tabBar->slots->region->extents.y1) - DAMAGE_BUFFER; reg.extents.x2 = MAX (group->tabBar->region->extents.x2, group->tabBar->revSlots->region->extents.x2) + DAMAGE_BUFFER; reg.extents.y2 = MAX (group->tabBar->region->extents.y2, group->tabBar->revSlots->region->extents.y2) + DAMAGE_BUFFER; damageScreenRegion (group->screen, ®); } void groupMoveTabBarRegion (GroupSelection *group, int dx, int dy, Bool syncIPW) { groupDamageTabBarRegion (group); XOffsetRegion (group->tabBar->region, dx, dy); if (syncIPW) XMoveWindow (group->screen->display->display, group->inputPrevention, group->tabBar->leftSpringX, group->tabBar->region->extents.y1); groupDamageTabBarRegion (group); } void groupResizeTabBarRegion (GroupSelection *group, XRectangle *box, Bool syncIPW) { int oldWidth; groupDamageTabBarRegion (group); oldWidth = group->tabBar->region->extents.x2 - group->tabBar->region->extents.x1; if (group->tabBar->bgLayer && oldWidth != box->width) { group->tabBar->bgLayer = groupRebuildCairoLayer (group->screen, group->tabBar->bgLayer, box->width + groupGetThumbSpace (group->screen) + groupGetThumbSize (group->screen), box->height); } EMPTY_REGION (group->tabBar->region); XUnionRectWithRegion(box, group->tabBar->region, group->tabBar->region); if (syncIPW) { XWindowChanges xwc; xwc.x = box->x; xwc.y = box->y; xwc.width = box->width; xwc.height = box->height; xwc.stack_mode = Above; xwc.sibling = HAS_TOP_WIN(group) ? TOP_TAB(group)->id : None; XConfigureWindow (group->screen->display->display, group->inputPrevention, CWSibling | CWStackMode | CWX | CWY | CWWidth | CWHeight, &xwc); } groupDamageTabBarRegion (group); } /* * groupInsertTabBarSlotBefore * */ void groupInsertTabBarSlotBefore(GroupTabBar *bar, GroupTabBarSlot *slot, GroupTabBarSlot *nextSlot) { GroupTabBarSlot *prev = nextSlot->prev; if(prev) { slot->prev = prev; prev->next = slot; } else { bar->slots = slot; slot->prev = NULL; } slot->next = nextSlot; nextSlot->prev = slot; bar->nSlots++; CompWindow *w = slot->window; GROUP_WINDOW(w); // Moving bar->region->extents.x1 / x2 as minX1 / maxX2 will work, // because the tab-bar got wider now, so it will put it in the average between them, // which is (bar->region->extents.x1 + bar->region->extents.x2) / 2 anyway. groupRecalcTabBarPos(gw->group, (bar->region->extents.x1 + bar->region->extents.x2) / 2, bar->region->extents.x1, bar->region->extents.x2); } /* * groupInsertTabBarSlotAfter * */ void groupInsertTabBarSlotAfter(GroupTabBar *bar, GroupTabBarSlot *slot, GroupTabBarSlot *prevSlot) { GroupTabBarSlot *next = prevSlot->next; if (next) { slot->next = next; next->prev = slot; } else { bar->revSlots = slot; slot->next = NULL; } slot->prev = prevSlot; prevSlot->next = slot; bar->nSlots++; CompWindow *w = slot->window; GROUP_WINDOW(w); // Moving bar->region->extents.x1 / x2 as minX1 / maxX2 will work, // because the tab-bar got wider now, so it will put it in the average between them, // which is (bar->region->extents.x1 + bar->region->extents.x2) / 2 anyway. groupRecalcTabBarPos(gw->group, (bar->region->extents.x1 + bar->region->extents.x2) / 2, bar->region->extents.x1, bar->region->extents.x2); } /* * groupInsertTabBarSlot * */ void groupInsertTabBarSlot(GroupTabBar *bar, GroupTabBarSlot *slot) { if (bar->slots != NULL) { bar->revSlots->next = slot; slot->prev = bar->revSlots; slot->next = NULL; } else { slot->prev = NULL; slot->next = NULL; bar->slots = slot; } bar->revSlots = slot; bar->nSlots++; CompWindow *w = slot->window; GROUP_WINDOW(w); // Moving bar->region->extents.x1 / x2 as minX1 / maxX2 will work, // because the tab-bar got wider now, so it will put it in the average between them, // which is (bar->region->extents.x1 + bar->region->extents.x2) / 2 anyway. groupRecalcTabBarPos(gw->group, (bar->region->extents.x1 + bar->region->extents.x2) / 2, bar->region->extents.x1, bar->region->extents.x2); } /* * groupUnhookTabBarSlot * */ void groupUnhookTabBarSlot(GroupTabBar *bar, GroupTabBarSlot *slot, Bool temporary) { GroupTabBarSlot *prev = slot->prev; GroupTabBarSlot *next = slot->next; if(prev) prev->next = next; else bar->slots = next; if(next) next->prev = prev; else bar->revSlots = prev; slot->prev = NULL; slot->next = NULL; bar->nSlots--; CompWindow *w = slot->window; GROUP_WINDOW(w); if (IS_TOP_TAB(w, gw->group) && !temporary) { if (next) groupChangeTab(next, RotateRight); else if (prev) groupChangeTab(prev, RotateLeft); else if (gw->group->nWins == 1) gw->group->topTab = NULL; if (groupGetUntabOnClose(w->screen)) groupUntabGroup(gw->group); } if(IS_PREV_TOP_TAB(w, gw->group) && !temporary) gw->group->prevTopTab = NULL; if (slot == bar->hoveredSlot) bar->hoveredSlot = NULL; if (slot == bar->textSlot) { bar->textSlot = NULL; if (bar->textLayer) { if (bar->textLayer->state == PaintFadeIn || bar->textLayer->state == PaintOn) { bar->textLayer->animationTime = (groupGetFadeTextTime(w->screen) * 1000) - bar->textLayer->animationTime; bar->textLayer->state = PaintFadeOut; } } } // Moving bar->region->extents.x1 / x2 as minX1 / maxX2 will work, // because the tab-bar got thiner now, so (bar->region->extents.x1 + bar->region->extents.x2) / 2 // Won't cause the new x1 / x2 to be outside the original region. groupRecalcTabBarPos(gw->group, (bar->region->extents.x1 + bar->region->extents.x2) / 2, bar->region->extents.x1, bar->region->extents.x2); } /* * groupDeleteTabBarSlot * */ void groupDeleteTabBarSlot(GroupTabBar *bar, GroupTabBarSlot *slot) { groupUnhookTabBarSlot(bar, slot, FALSE); if (slot->region) XDestroyRegion(slot->region); CompWindow *w = slot->window; CompScreen *s = w->screen; GROUP_WINDOW(w); GROUP_SCREEN(s); if(slot == gs->draggedSlot) { gs->draggedSlot = NULL; gs->dragged = FALSE; if (gs->grabState == ScreenGrabTabDrag) groupGrabScreen(s, ScreenGrabNone); } gw->slot = NULL; groupUpdateWindowProperty(w); free(slot); } /* * groupCreateSlot * */ void groupCreateSlot(GroupSelection *group, CompWindow *w) { if(!group->tabBar) return; GROUP_WINDOW(w); GroupTabBarSlot *slot = (GroupTabBarSlot*) malloc(sizeof(GroupTabBarSlot)); slot->window = w; slot->region = XCreateRegion(); groupInsertTabBarSlot(group->tabBar, slot); gw->slot = slot; groupUpdateWindowProperty(w); } #define SPRING_K groupGetDragSpringK(s) #define FRICTION groupGetDragFriction(s) #define SIZE groupGetThumbSize(s) #define BORDER groupGetBorderRadius(s) #define Y_START_MOVE groupGetDragYDistance(s) #define SPEED_LIMIT groupGetDragSpeedLimit(s) /* * groupSpringForce * */ static int groupSpringForce(CompScreen *s, int centerX, int springX) { // Each slot has a spring attached to it, starting at springX, and ending at the center of the slot (centerX). // The spring will cause the slot to move, using the well-known physical formula F = k * dl... return -SPRING_K * (centerX - springX); } /* * groupDraggedSlotForce * */ static int groupDraggedSlotForce(CompScreen *s, int distanceX, int distanceY) { // The dragged slot will make the slot move, to get DnD animations (slots will make room for the newly inserted slot). // As the dragged slot is closer to the slot, it will put more force on the slot, causing it to make room for the dragged slot... // But if the dragged slot gets too close to the slot, they are going to be reordered soon, so the force will get lower. // If the dragged slot is in the other side of the slot, it will have to make force in the opposite direction. // So we the needed funtion is an odd function that goes up at first, and down after that. // Sinus is a function like that... :) // The maximum is got when x = (x1 + x2) / 2, in this case: x = SIZE + BORDER. // Because of that, for x = SIZE + BORDER, we get a force of SPRING_K * (SIZE + BORDER) / 2. // That equals to the force we get from the the spring. // This way, the slot won't move when its distance from the dragged slot is SIZE + BORDER (which is the default distance between slots). float a = SPRING_K * (SIZE + BORDER) / 2; // The maximum value. float b = PI / (2 * SIZE + 2 * BORDER); // This will make distanceX == 2 * (SIZE + BORDER) to get 0, // and distanceX == (SIZE + BORDER) to get the maximum. // If there is some distance between the slots in the y axis, the slot should get less force... // For this, we change max to a lower value, using a simple linear function. if(distanceY < Y_START_MOVE) a *= 1.0f - (float)distanceY / Y_START_MOVE; else a = 0; if(abs(distanceX) < 2 * (SIZE + BORDER)) return a * sin(b * distanceX); else return 0; } /* * groupApplyFriction * */ static void groupApplyFriction(CompScreen *s, int *speed) { if(abs(*speed) < FRICTION) *speed = 0; else if(*speed > 0) *speed -= FRICTION; else if(*speed < 0) *speed += FRICTION; } /* * groupApplySpeedLimit * */ static void groupApplySpeedLimit(CompScreen *s, int *speed) { if(*speed > SPEED_LIMIT) *speed = SPEED_LIMIT; else if(*speed < -SPEED_LIMIT) *speed = - SPEED_LIMIT; } /* * groupApplyForces * */ void groupApplyForces(CompScreen *s, GroupTabBar *bar, GroupTabBarSlot* draggedSlot) { GroupTabBarSlot* slot, *slot2; int centerX, centerY; int draggedCenterX, draggedCenterY; if (draggedSlot) { int vx, vy; groupGetDrawOffsetForSlot(draggedSlot, &vx, &vy); draggedCenterX = ((draggedSlot->region->extents.x1 + draggedSlot->region->extents.x2) / 2) + vx; draggedCenterY = ((draggedSlot->region->extents.y1 + draggedSlot->region->extents.y2) / 2) + vy; } else { draggedCenterX = 0; draggedCenterY = 0; } bar->leftSpeed += groupSpringForce(s, bar->region->extents.x1, bar->leftSpringX); bar->rightSpeed += groupSpringForce(s, bar->region->extents.x2, bar->rightSpringX); if (draggedSlot) { int leftForce = groupDraggedSlotForce(s, bar->region->extents.x1 - SIZE / 2 - draggedCenterX, abs((bar->region->extents.y1 + bar->region->extents.y2) / 2 - draggedCenterY)); int rightForce = groupDraggedSlotForce(s, bar->region->extents.x2 + SIZE / 2 - draggedCenterX, abs((bar->region->extents.y1 + bar->region->extents.y2) / 2 - draggedCenterY)); if(leftForce < 0) bar->leftSpeed += leftForce; if(rightForce > 0) bar->rightSpeed += rightForce; } for(slot = bar->slots; slot; slot = slot->next) { centerX = (slot->region->extents.x1 + slot->region->extents.x2) / 2; centerY = (slot->region->extents.y1 + slot->region->extents.y2) / 2; slot->speed += groupSpringForce(s, centerX, slot->springX); if(draggedSlot && draggedSlot != slot) { int draggedSlotForce = groupDraggedSlotForce(s, centerX - draggedCenterX, abs(centerY - draggedCenterY)); slot->speed += draggedSlotForce ; slot2 = NULL; if(draggedSlotForce < 0) { slot2 = slot->prev; bar->leftSpeed += draggedSlotForce; } else if(draggedSlotForce > 0) { slot2 = slot->next; bar->rightSpeed += draggedSlotForce; } for( ; slot2; slot2 = (draggedSlotForce < 0)? slot2->prev: slot2->next) { if(slot2 != draggedSlot) slot2->speed += draggedSlotForce; } } } for(slot = bar->slots; slot; slot = slot->next) { groupApplyFriction(s, &slot->speed); groupApplySpeedLimit(s, &slot->speed); } groupApplyFriction(s, &bar->leftSpeed); groupApplySpeedLimit(s, &bar->leftSpeed); groupApplyFriction(s, &bar->rightSpeed); groupApplySpeedLimit(s, &bar->rightSpeed); } /* * groupApplySpeeds * */ void groupApplySpeeds(CompScreen* s, GroupSelection *group, int msSinceLastRepaint) { GroupTabBar *bar = group->tabBar; GroupTabBarSlot* slot; int move; XRectangle box; Bool updateTabBar = FALSE; box.x = bar->region->extents.x1; box.y = bar->region->extents.y1; box.width = bar->region->extents.x2 - bar->region->extents.x1; box.height = bar->region->extents.y2 - bar->region->extents.y1; bar->leftMsSinceLastMove += msSinceLastRepaint; bar->rightMsSinceLastMove += msSinceLastRepaint; // Left move = bar->leftSpeed * bar->leftMsSinceLastMove / 1000; if(move) { box.x += move; box.width -= move; bar->leftMsSinceLastMove = 0; updateTabBar = TRUE; } else if(bar->leftSpeed == 0 && bar->region->extents.x1 != bar->leftSpringX && SPRING_K * abs(bar->region->extents.x1 - bar->leftSpringX) < FRICTION) { // Friction is preventing from the left border to get to its original position. box.x += bar->leftSpringX - bar->region->extents.x1; box.width -= bar->leftSpringX - bar->region->extents.x1; bar->leftMsSinceLastMove = 0; updateTabBar = TRUE; } else if(bar->leftSpeed == 0) bar->leftMsSinceLastMove = 0; //Right move = bar->rightSpeed * bar->rightMsSinceLastMove / 1000; if(move) { box.width += move; bar->rightMsSinceLastMove = 0; updateTabBar = TRUE; } else if(bar->rightSpeed == 0 && bar->region->extents.x2 != bar->rightSpringX && SPRING_K * abs(bar->region->extents.x2 - bar->rightSpringX) < FRICTION) { // Friction is preventing from the right border to get to its original position. box.width += bar->leftSpringX - bar->region->extents.x1; bar->leftMsSinceLastMove = 0; updateTabBar = TRUE; } else if(bar->rightSpeed == 0) bar->rightMsSinceLastMove = 0; if(updateTabBar) groupResizeTabBarRegion (group, &box, FALSE); for(slot = bar->slots; slot; slot = slot->next) { slot->msSinceLastMove += msSinceLastRepaint; move = slot->speed * slot->msSinceLastMove / 1000; if(move) { XOffsetRegion (slot->region, move, 0); slot->msSinceLastMove = 0; } else if(slot->speed == 0 && (slot->region->extents.x1 + slot->region->extents.x2) / 2 != slot->springX && SPRING_K * abs((slot->region->extents.x1 + slot->region->extents.x2) / 2 - slot->springX) < FRICTION) { // Friction is preventing from the slot to get to its original position. XOffsetRegion (slot->region, slot->springX - (slot->region->extents.x1 + slot->region->extents.x2) / 2, 0); slot->msSinceLastMove = 0; } else if(slot->speed == 0) slot->msSinceLastMove = 0; } } /* * groupInitTabBar * */ void groupInitTabBar(GroupSelection *group, CompWindow* topTab) { // error if (group->tabBar) return; GroupTabBar *bar = (GroupTabBar*) malloc(sizeof(GroupTabBar)); bar->slots = NULL; bar->nSlots = 0; bar->bgAnimation = AnimationNone; bar->bgAnimationTime = 0; bar->state = PaintOff; bar->animationTime = 0; bar->timeoutHandle = 0; bar->textLayer = NULL; bar->bgLayer = NULL; bar->selectionLayer = NULL; bar->hoveredSlot = NULL; bar->textSlot = NULL; bar->oldWidth = 0; group->tabBar = bar; bar->region = XCreateRegion(); int i; for(i = 0; i < group->nWins; i++) groupCreateSlot(group, group->windows[i]); groupRecalcTabBarPos(group, WIN_X(topTab) + WIN_WIDTH(topTab) / 2, WIN_X(topTab), WIN_X(topTab) + WIN_WIDTH(topTab)); } /* * groupDeleteTabBar * */ void groupDeleteTabBar(GroupSelection *group) { GroupTabBar *bar = group->tabBar; groupDestroyCairoLayer(group->screen, bar->textLayer); groupDestroyCairoLayer(group->screen, bar->bgLayer); groupDestroyCairoLayer(group->screen, bar->selectionLayer); groupDestroyInputPreventionWindow(group); if (bar->timeoutHandle) compRemoveTimeout(bar->timeoutHandle); while (bar->slots) groupDeleteTabBarSlot(bar, bar->slots); if (bar->region) XDestroyRegion(bar->region); free(bar); group->tabBar = NULL; } /* * groupInitTab * */ Bool groupInitTab(CompDisplay * d, CompAction * action, CompActionState state, CompOption * option, int nOption) { CompWindow *w = findWindowAtDisplay(d, d->activeWindow); Bool allowUntab = TRUE; if (!w) return TRUE; GROUP_WINDOW(w); if (gw->inSelection) { groupGroupWindows(d, action, state, option, nOption); allowUntab = FALSE; // If the window was selected, we don't want to untab the group. // because the user probably wanted to tab the selected windows. } if (!gw->group) return TRUE; if (gw->group->tabbingState != PaintOff) groupSyncWindows(gw->group); if (!gw->group->tabBar) { groupTabGroup(w); } else if (allowUntab) { groupUntabGroup(gw->group); } damageScreen(w->screen); return TRUE; } /* * groupChangeTabLeft * */ Bool groupChangeTabLeft(CompDisplay * d, CompAction * action, CompActionState state, CompOption * option, int nOption) { CompWindow *w = findWindowAtDisplay(d, d->activeWindow); CompWindow *topTab = w; if(!w) return TRUE; GROUP_WINDOW(w); GROUP_SCREEN(w->screen); if(!gw->slot || !gw->group) return TRUE; if(gw->group->nextTopTab) topTab = NEXT_TOP_TAB(gw->group); else if(gw->group->topTab) topTab = TOP_TAB(gw->group); //If there are no tabbing animations, topTab is never NULL. gw = GET_GROUP_WINDOW(topTab, gs); if(gw->slot->prev) return groupChangeTab(gw->slot->prev, RotateLeft); else return groupChangeTab(gw->group->tabBar->revSlots, RotateLeft); } /* * groupChangeTabRight * */ Bool groupChangeTabRight(CompDisplay * d, CompAction * action, CompActionState state, CompOption * option, int nOption) { CompWindow *w = findWindowAtDisplay(d, d->activeWindow); CompWindow *topTab = w; if(!w) return TRUE; GROUP_WINDOW(w); GROUP_SCREEN(w->screen); if(!gw->slot || !gw->group) return TRUE; if(gw->group->nextTopTab) topTab = NEXT_TOP_TAB(gw->group); else if(gw->group->topTab) topTab = TOP_TAB(gw->group); //If there are no tabbing animations, topTab is never NULL. gw = GET_GROUP_WINDOW(topTab, gs); if(gw->slot->next) return groupChangeTab(gw->slot->next, RotateRight); else return groupChangeTab(gw->group->tabBar->slots, RotateRight); } /* * groupSwitchTopTabInput * */ void groupSwitchTopTabInput(GroupSelection *group, Bool enable) { if(!group->tabBar || !HAS_TOP_WIN(group)) return; if (!group->inputPrevention) groupCreateInputPreventionWindow(group); if (!enable) XMapWindow(group->screen->display->display, group->inputPrevention); else XUnmapWindow(group->screen->display->display, group->inputPrevention); group->ipwMapped = !enable; } /* * groupCreateInputPreventionWindow * */ void groupCreateInputPreventionWindow(GroupSelection* group) { if (!group->inputPrevention) //For preventing a memory leak. { XSetWindowAttributes attrib; attrib.override_redirect = TRUE; group->inputPrevention = XCreateWindow(group->screen->display->display, group->screen->root, -100, -100, 1, 1, 0, CopyFromParent, InputOnly, CopyFromParent, CWOverrideRedirect, &attrib); group->ipwMapped = FALSE; } } /* * groupDestroyInputPreventionWindow * */ void groupDestroyInputPreventionWindow(GroupSelection* group) { if(group->inputPrevention) { XDestroyWindow(group->screen->display->display, group->inputPrevention); group->inputPrevention = None; group->ipwMapped = TRUE; } }