#include "group.h" /** * * Beryl group plugin * * group.c * * Copyright : (C) 2006 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. * **/ /* helper for activating a window after a certain time a slot has dragged over it */ static Bool groupDragHoverTimeout(void* closure) { CompWindow *w = (CompWindow *) closure; if (!w) return FALSE; GROUP_SCREEN(w->screen); activateWindow(w); gs->dragHoverTimeoutHandle = 0; return FALSE; } /* * groupFindWindowIndex * */ int groupFindWindowIndex(CompWindow *w, GroupSelection *g) { int i; for(i = 0; i < g->nWins; i++) { if(g->windows[i]->id == w->id) return i; } return -1; } /* * groupGrabScreen * */ void groupGrabScreen(CompScreen * s, GroupScreenGrabState newState) { GROUP_SCREEN(s); if ((gs->grabState != newState) && gs->grabIndex) { removeScreenGrab(s, gs->grabIndex, NULL); gs->grabIndex = 0; } if (newState == ScreenGrabSelect) { gs->grabIndex = pushScreenGrab(s, None, "group"); } else if (newState == ScreenGrabTabDrag) { gs->grabIndex = pushScreenGrab(s, None, "group-drag"); } gs->grabState = newState; } /* * groupSyncWindows * */ void groupSyncWindows(GroupSelection *group) { int i; for (i = 0; i < group->nWins; i++) { CompWindow *w = group->windows[i]; syncWindowPosition(w); } } /* * groupRaiseWindows * */ void groupRaiseWindows(CompWindow * top, GroupSelection *group) { int i; for (i = 0; i < group->nWins; i++) { CompWindow *w = group->windows[i]; if (w->id == top->id) continue; restackWindowBelow(w, top); } } /* * groupMinimizeWindows * */ static void groupMinimizeWindows(CompWindow *top, GroupSelection *group, Bool minimize) { int i; for (i = 0; i < group->nWins; i++) { CompWindow *w = group->windows[i]; if (w->id == top->id) continue; if (minimize) minimizeWindow(w); else unminimizeWindow(w); } } /* * groupShadeWindows * */ static void groupShadeWindows(CompWindow *top, GroupSelection *group, Bool shade) { int i; for (i = 0; i < group->nWins; i++) { CompWindow *w = group->windows[i]; if (w->id == top->id) continue; if (shade) w->state |= CompWindowStateShadedMask; else w->state &= ~CompWindowStateShadedMask; updateWindowAttributes(w, FALSE); } } /* * groupDeleteGroupWindow * * Note: allowRegroup is need for a special case when groupAddWindowToGroup * calls this function. * */ void groupDeleteGroupWindow(CompWindow * w, Bool allowRegroup) { GROUP_WINDOW(w); GROUP_SCREEN(w->screen); GroupSelection *group = gw->group; if (!group) return; if (group->tabBar) { if (gw->slot) { if (gs->draggedSlot && gs->dragged && gs->draggedSlot->window->id == w->id) groupUnhookTabBarSlot(group->tabBar, gw->slot, FALSE); else groupDeleteTabBarSlot(group->tabBar, gw->slot); } if(!gw->ungroup && group->nWins > 1) { if(HAS_TOP_WIN(group)) { // TODO: maybe add the IS_ANIMATED to the topTab for better constraining... CompWindow *topTab = TOP_TAB(group); GroupWindow *gtw = GET_GROUP_WINDOW(topTab, gs); int oldX = gw->orgPos.x; int oldY = gw->orgPos.y; gw->orgPos.x = group->oldTopTabCenterX - WIN_WIDTH(w) / 2; gw->orgPos.y = group->oldTopTabCenterY - WIN_HEIGHT(w) / 2; gw->destination.x = group->oldTopTabCenterX - WIN_WIDTH(w)/2 + gw->mainTabOffset.x - gtw->mainTabOffset.x; gw->destination.y = group->oldTopTabCenterY - WIN_HEIGHT(w)/2 + gw->mainTabOffset.y - gtw->mainTabOffset.y; gw->mainTabOffset.x = oldX; gw->mainTabOffset.y = oldY; gw->animateState |= IS_ANIMATED; gw->tx = gw->ty = gw->xVelocity = gw->yVelocity = 0.0f; } // Although when there is no top-tab, it will never really animate anything, // if we don't start the animation, the window will never get remvoed. groupStartTabbingAnimation(group, FALSE); group->ungroupState = UngroupSingle; gw->ungroup = TRUE; return; } } if (group->nWins && group->windows) { CompWindow **buf = group->windows; group->windows = (CompWindow **) calloc(group->nWins - 1, sizeof(CompWindow *)); int counter = 0; int i; for (i = 0; i < group->nWins; i++) { if (buf[i]->id == w->id) continue; group->windows[counter++] = buf[i]; } group->nWins = counter; if (group->nWins == 1) { damageWindowOutputExtents(group->windows[0]); // Glow was removed from this window too. updateWindowOutputExtents(group->windows[0]); } if (group->nWins == 1 && gs->opt[GROUP_SCREEN_OPTION_AUTO_UNGROUP].value.b) { if (group->changeTab) { /* a change animation is pending: this most likely means that a window must be moved back onscreen, so we do that here */ CompWindow *lw = group->windows[0]; gs->queued = TRUE; moveWindowOnscreen(lw); moveWindow(lw, group->oldTopTabCenterX - WIN_X(lw) - WIN_WIDTH(lw) / 2, group->oldTopTabCenterY - WIN_Y(lw) - WIN_HEIGHT(lw) / 2, TRUE, TRUE); syncWindowPosition(lw); gs->queued = FALSE; } groupDeleteGroup(group); } else if (group->nWins <= 0) { free(group->windows); group->windows = NULL; groupDeleteGroup(group); } free(buf); damageWindowOutputExtents(w); gw->group = NULL; updateWindowOutputExtents(w); if (allowRegroup && gs->opt[GROUP_SCREEN_OPTION_AUTOTAB].value.b && (w->type & gs->wMask)) { groupAddWindowToGroup(w, NULL); groupTabGroup(w); } } } /* * groupDeleteGroup * */ void groupDeleteGroup(GroupSelection *group) { GROUP_SCREEN(group->screen); if (group->windows != NULL) { if (group->tabBar) { groupUntabGroup(group); group->ungroupState = UngroupAll; return; } int i; for (i = 0; i < group->nWins; i++) { CompWindow *cw = group->windows[i]; GROUP_WINDOW(cw); GROUP_SCREEN(cw->screen); damageWindowOutputExtents(cw); gw->group = NULL; updateWindowOutputExtents(cw); if (gs->opt[GROUP_SCREEN_OPTION_AUTOTAB].value.b && (cw->type & gs->wMask)) { groupAddWindowToGroup(cw, NULL); groupTabGroup(cw); } } free(group->windows); group->windows = NULL; } else if (group->tabBar) groupDeleteTabBar(group); GroupSelection *prev = group->prev; GroupSelection *next = group->next; // relink stack if (prev || next) { if (prev) { if (next) prev->next = next; else { prev->next = NULL; } } if (next) { if (prev) next->prev = prev; else { next->prev = NULL; gs->groups = next; } } } else { gs->groups = NULL; } free(group); } /* * groupAddWindowToGroup * */ void groupAddWindowToGroup(CompWindow * w, GroupSelection *group) { GROUP_SCREEN(w->screen); GROUP_WINDOW(w); if(group && (gw->group == group)) return; if (gw->group) { gw->ungroup = TRUE; //This will prevent setting up animations on the previous group. groupDeleteGroupWindow(w, FALSE); gw->ungroup = FALSE; } if (group) { group->windows = (CompWindow **) realloc(group->windows, sizeof(CompWindow *) * (group->nWins + 1)); group->windows[group->nWins] = w; group->nWins++; gw->group = group; updateWindowOutputExtents(w); if(group->nWins == 2) updateWindowOutputExtents(group->windows[0]); // First window in the group got its glow too... if (group->tabBar && group->topTab) { CompWindow *topTab = TOP_TAB(group); if(!gw->slot) groupCreateSlot(group, w); gw->destination.x = WIN_X(topTab) + (WIN_WIDTH(topTab) / 2) - (WIN_WIDTH(w) / 2); gw->destination.y = WIN_Y(topTab) + (WIN_HEIGHT(topTab) / 2) - (WIN_HEIGHT(w) / 2); gw->mainTabOffset.x = WIN_X(w) - gw->destination.x; gw->mainTabOffset.y = WIN_Y(w) - gw->destination.y; gw->orgPos.x = WIN_X(w); gw->orgPos.y = WIN_Y(w); gw->tx = gw->ty = gw->xVelocity = gw->yVelocity = 0.0f; gw->animateState = IS_ANIMATED; groupStartTabbingAnimation(group, TRUE); damageScreen(w->screen); } } else { GroupSelection *g = malloc(sizeof(GroupSelection)); g->windows = (CompWindow **) calloc(1, sizeof(CompWindow *)); g->windows[0] = w; g->screen = w->screen; g->nWins = 1; g->topTab = NULL; g->prevTopTab = NULL; g->nextTopTab = NULL; g->activateTab = NULL; g->doTabbing = FALSE; g->changeAnimationTime = 0; g->changeAnimationDirection = 0; g->changeState = PaintOff; g->tabbingState = PaintOff; g->changeTab = FALSE; g->ungroupState = UngroupNone; g->tabBar = NULL; g->grabWindow = None; g->grabMask = 0; g->inputPrevention = None; g->oldTopTabCenterX = 0; g->oldTopTabCenterY = 0; // glow color srand(time(NULL)); g->color[0] = rand() % 0xFFFF; g->color[1] = rand() % 0xFFFF; g->color[2] = rand() % 0xFFFF; g->color[3] = 0xFFFF; // relink stack if (gs->groups) { gs->groups->prev = g; g->next = gs->groups; g->prev = NULL; gs->groups = g; } else { g->prev = NULL; g->next = NULL; gs->groups = g; } gw->group = g; } } /* * groupGroupWindows * */ Bool groupGroupWindows(CompDisplay * d, CompAction * action, CompActionState state, CompOption * option, int nOption) { GROUP_DISPLAY(d); if (gd->tmpSel.nWins > 0) { int i; CompWindow *cw; GroupSelection *group = NULL; Bool tabbed = FALSE; for(i = 0; i < gd->tmpSel.nWins; i++) { cw = gd->tmpSel.windows[i]; GROUP_WINDOW(cw); if (gw->group) { if(!tabbed || group->tabBar) group = gw->group; if(group->tabBar) tabbed = TRUE; } } // we need to do one first to get the pointer of a new group cw = gd->tmpSel.windows[0]; groupAddWindowToGroup(cw, group); GROUP_WINDOW (cw); gw->inSelection = FALSE; damageScreen(cw->screen); group = gw->group; for (i = 1; i < gd->tmpSel.nWins; i++) { cw = gd->tmpSel.windows[i]; GROUP_WINDOW(cw); groupAddWindowToGroup(cw, group); gw->inSelection = FALSE; damageScreen(cw->screen); } // exit selection free(gd->tmpSel.windows); gd->tmpSel.windows = NULL; gd->tmpSel.nWins = 0; } return FALSE; } /* * groupUnGroupWindows * */ Bool groupUnGroupWindows(CompDisplay * d, CompAction * action, CompActionState state, CompOption * option, int nOption) { CompWindow *cw = findWindowAtDisplay(d, d->activeWindow); if (!cw) return FALSE; GROUP_WINDOW(cw); if (gw->group) { groupDeleteGroup(gw->group); } return FALSE; } /* * groupRemoveWindow * */ Bool groupRemoveWindow(CompDisplay * d, CompAction * action, CompActionState state, CompOption * option, int nOption) { CompWindow *cw = findWindowAtDisplay(d, d->activeWindow); if (!cw) return FALSE; GROUP_WINDOW(cw); if (gw->group) groupDeleteGroupWindow(cw, TRUE); return FALSE; } /* * groupCloseWindows * */ Bool groupCloseWindows(CompDisplay * d, CompAction * action, CompActionState state, CompOption * option, int nOption) { CompWindow *w = findWindowAtDisplay(d, d->activeWindow); if (!w) return FALSE; GROUP_WINDOW(w); if (gw->group) { int nWins = gw->group->nWins; int i; for (i = 0; i < nWins; i++) { CompWindow *cw = gw->group->windows[i]; closeWindow(cw, getCurrentTimeFromDisplay(d)); } } return FALSE; } /* * groupChangeColor * */ Bool groupChangeColor(CompDisplay * d, CompAction * action, CompActionState state, CompOption * option, int nOption) { CompWindow *w = findWindowAtDisplay(d, d->activeWindow); if (!w) return FALSE; GROUP_WINDOW(w); if (gw->group) { srand(time(NULL)); gw->group->color[0] = rand() % 0xFFFF; gw->group->color[1] = rand() % 0xFFFF; gw->group->color[2] = rand() % 0xFFFF; groupRenderTopTabHighlight(gw->group); damageScreen(w->screen); } return FALSE; } /* * groupSetIgnore * */ Bool groupSetIgnore(CompDisplay * d, CompAction * action, CompActionState state, CompOption * option, int nOption) { GROUP_DISPLAY(d); gd->ignoreMode = TRUE; if (state & CompActionStateInitKey) action->state |= CompActionStateTermKey; return FALSE; } /* * groupUnsetIgnore * */ Bool groupUnsetIgnore(CompDisplay * d, CompAction * action, CompActionState state, CompOption * option, int nOption) { GROUP_DISPLAY(d); gd->ignoreMode = FALSE; action->state &= ~CompActionStateTermKey; return FALSE; } /* * groupHandleButtonPressEvent * */ static void groupHandleButtonPressEvent(CompDisplay *d, XEvent *event) { int xRoot = event->xbutton.x_root; int yRoot = event->xbutton.y_root; int button = event->xbutton.button; CompScreen *s = findScreenAtDisplay(d, event->xbutton.root); if (!s) return; GROUP_SCREEN(s); if (button == 1) { GroupSelection *group; for (group = gs->groups; group; group = group->next) { if ((group->inputPrevention == event->xbutton.window) && group->tabBar) { GroupTabBarSlot *slot; for (slot = group->tabBar->slots; slot; slot = slot->next) { if (XPointInRegion (slot->region, xRoot, yRoot)) { CompScreen *s = group->screen; GROUP_SCREEN(s); gs->draggedSlot = slot; gs->dragged = FALSE; // The slot isn't dragged yet... gs->prevX = xRoot; gs->prevY = yRoot; if (gs->grabState == ScreenGrabNone) { if (!otherScreenGrabExist(s, "group", "group-drag")) groupGrabScreen(s, ScreenGrabTabDrag); } } } } } } else if ((button == 4) || (button == 5)) { CompWindow *w = findWindowAtDisplay(d, d->activeWindow); CompWindow *topTab = w; if(!w) return; GROUP_WINDOW(w); GROUP_SCREEN(w->screen); if(!gw->slot || !gw->group || !gw->group->tabBar) return; if(gw->group->tabBar->state != PaintOn && gw->group->tabBar->state != PaintPermanentOn) return; if(!XPointInRegion(gw->group->tabBar->region, xRoot, yRoot)) return; 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 (button == 4) { if(gw->slot->prev) groupChangeTab(gw->slot->prev, RotateLeft); else groupChangeTab(gw->group->tabBar->revSlots, RotateLeft); } else { if(gw->slot->next) groupChangeTab(gw->slot->next, RotateRight); else groupChangeTab(gw->group->tabBar->slots, RotateRight); } } } /* * groupHandleButtonReleaseEvent * */ static void groupHandleButtonReleaseEvent(CompDisplay *d, XEvent *event) { GroupSelection *group; if (event->xbutton.button != 1) return; CompScreen *s = findScreenAtDisplay(d, event->xbutton.root); if (!s) return; GROUP_SCREEN(s); if(!gs->draggedSlot) return; if(!gs->dragged) { groupChangeTab(gs->draggedSlot, RotateUncertain); gs->draggedSlot = NULL; if (gs->grabState == ScreenGrabTabDrag) groupGrabScreen(s, ScreenGrabNone); return; } GROUP_WINDOW(gs->draggedSlot->window); int vx, vy; Region newRegion = XCreateRegion(); XUnionRegion(newRegion, gs->draggedSlot->region, newRegion); groupGetDrawOffsetForSlot(gs->draggedSlot, &vx, &vy); XOffsetRegion(newRegion, vx, vy); Bool inserted = FALSE; for(group = gs->groups; group; group = group->next) { Bool inTabBar; if (!group->tabBar) continue; // create clipping region Region clip = groupGetClippingRegion(TOP_TAB(group)); Region buf = XCreateRegion(); XIntersectRegion(newRegion, group->tabBar->region, buf); XSubtractRegion(buf, clip, buf); XDestroyRegion(clip); inTabBar = !XEmptyRegion(buf); XDestroyRegion(buf); if(!inTabBar) continue; GroupTabBarSlot *slot; for(slot = group->tabBar->slots; slot; slot = slot->next) { if(slot == gs->draggedSlot) continue; Region slotRegion = XCreateRegion(); XRectangle rect; Bool inSlot; if(slot->prev && slot->prev != gs->draggedSlot) rect.x = slot->prev->region->extents.x2; else if(slot->prev && slot->prev == gs->draggedSlot && gs->draggedSlot->prev) rect.x = gs->draggedSlot->prev->region->extents.x2; else rect.x = group->tabBar->region->extents.x1; rect.y = slot->region->extents.y1; if(slot->next && slot->next != gs->draggedSlot) rect.width = slot->next->region->extents.x1 - rect.x; else if(slot->next && slot->next == gs->draggedSlot && gs->draggedSlot->next) rect.width = gs->draggedSlot->next->region->extents.x1 - rect.x; else rect.width = group->tabBar->region->extents.x2; rect.height = slot->region->extents.y2 - slot->region->extents.y1; XUnionRectWithRegion(&rect, slotRegion, slotRegion); Region buf = XCreateRegion(); XIntersectRegion(newRegion, slotRegion, buf); inSlot = !XEmptyRegion(buf); XDestroyRegion(buf); XDestroyRegion(slotRegion); if (!inSlot) continue; GroupTabBarSlot *tmpDraggedSlot = gs->draggedSlot; if(group != gw->group) { GroupSelection *tmpGroup = gw->group; // if the dragged window is not the top tab, move it onscreen if (!IS_TOP_TAB(gs->draggedSlot->window, tmpGroup) && tmpGroup->topTab) { CompWindow *tw = tmpGroup->topTab->window; tmpGroup->oldTopTabCenterX = WIN_X(tw) + WIN_WIDTH(tw) / 2; tmpGroup->oldTopTabCenterY = WIN_Y(tw) + WIN_HEIGHT(tw) / 2; gs->queued = TRUE; moveWindowOnscreen(gs->draggedSlot->window); moveWindow(gs->draggedSlot->window, gw->group->oldTopTabCenterX - WIN_X(gs->draggedSlot->window) - WIN_WIDTH(gs->draggedSlot->window) / 2, gw->group->oldTopTabCenterY - WIN_Y(gs->draggedSlot->window) - WIN_HEIGHT(gs->draggedSlot->window) / 2, TRUE, TRUE); syncWindowPosition(gs->draggedSlot->window); gs->queued = FALSE; } // Change the group. groupAddWindowToGroup(gs->draggedSlot->window, group); } else groupUnhookTabBarSlot(group->tabBar, gs->draggedSlot, TRUE); // for re-calculating the tab-bar including the dragged window gs->draggedSlot = NULL; gs->dragged = FALSE; inserted = TRUE; if((tmpDraggedSlot->region->extents.x1 + tmpDraggedSlot->region->extents.x2 + (2 * vx)) / 2 > (slot->region->extents.x1 + slot->region->extents.x2) / 2) groupInsertTabBarSlotAfter(group->tabBar, tmpDraggedSlot, slot); else groupInsertTabBarSlotBefore(group->tabBar, tmpDraggedSlot, slot); // Hide tab-bars. GroupSelection *tmpGroup; for(tmpGroup = gs->groups; tmpGroup; tmpGroup = tmpGroup->next) groupTabSetVisibility(tmpGroup, FALSE, PERMANENT); break; } if (inserted) break; } if (newRegion) XDestroyRegion(newRegion); if (!inserted) { CompWindow *draggedSlotWindow = gs->draggedSlot->window; GroupSelection *tmpGroup; for(tmpGroup = gs->groups; tmpGroup; tmpGroup = tmpGroup->next) groupTabSetVisibility(tmpGroup, FALSE, PERMANENT); gs->draggedSlot = NULL; gs->dragged = FALSE; if(gs->opt[GROUP_SCREEN_OPTION_DND_UNGROUP_WINDOW].value.b) groupDeleteGroupWindow(draggedSlotWindow, TRUE); else if (gw->group && gw->group->topTab) { groupRecalcTabBarPos(gw->group, (gw->group->tabBar->region->extents.x1 + gw->group->tabBar->region->extents.x2) / 2, gw->group->tabBar->region->extents.x1, gw->group->tabBar->region->extents.x2); } // to remove the painted slot damageScreen(s); } if (gs->grabState == ScreenGrabTabDrag) groupGrabScreen(s, ScreenGrabNone); if (gs->dragHoverTimeoutHandle) { compRemoveTimeout(gs->dragHoverTimeoutHandle); gs->dragHoverTimeoutHandle = 0; } } /* * groupHandleMotionEvent * */ /* the radius to determine if it was a click or a drag */ #define RADIUS 5 static void groupHandleMotionEvent(CompScreen * s, int xRoot, int yRoot) { GROUP_SCREEN(s); if (gs->grabState == ScreenGrabTabDrag) { int dx, dy; REGION reg; Region draggedRegion = gs->draggedSlot->region; reg.rects = ®.extents; reg.numRects = 1; dx = xRoot - gs->prevX; dy = yRoot - gs->prevY; if(gs->dragged || abs(dx) > RADIUS || abs(dy) > RADIUS) { gs->prevX = xRoot; gs->prevY = yRoot; if(!gs->dragged) { GroupSelection *group; GROUP_WINDOW(gs->draggedSlot->window); gs->dragged = TRUE; for(group = gs->groups; group; group = group->next) groupTabSetVisibility(group, TRUE, PERMANENT); groupRecalcTabBarPos(gw->group, (gw->group->tabBar->region->extents.x1 + gw->group->tabBar->region->extents.x2) / 2, gw->group->tabBar->region->extents.x1, gw->group->tabBar->region->extents.x2); } int vx,vy; groupGetDrawOffsetForSlot(gs->draggedSlot, &vx, &vy); // (border = 5) + (buffer = 2) == (damage buffer = 7) //TODO: respect border option. reg.extents.x1 = draggedRegion->extents.x1 - 7 + vx; reg.extents.y1 = draggedRegion->extents.y1 - 7 + vy; reg.extents.x2 = draggedRegion->extents.x2 + 7 + vx; reg.extents.y2 = draggedRegion->extents.y2 + 7 + vy; damageScreenRegion(s, ®); XOffsetRegion (gs->draggedSlot->region, dx, dy); gs->draggedSlot->springX = (gs->draggedSlot->region->extents.x1 + gs->draggedSlot->region->extents.x2) / 2; reg.extents.x1 = draggedRegion->extents.x1 - 7 + vx; reg.extents.y1 = draggedRegion->extents.y1 - 7 + vy; reg.extents.x2 = draggedRegion->extents.x2 + 7 + vx; reg.extents.y2 = draggedRegion->extents.y2 + 7 + vy; damageScreenRegion(s, ®); } } else if (gs->grabState == ScreenGrabSelect) { groupDamageSelectionRect(s, xRoot, yRoot); } } /* * groupHandleEvent * */ void groupHandleEvent(CompDisplay * d, XEvent * event) { GROUP_DISPLAY(d); switch (event->type) { case MotionNotify: { CompScreen *s = findScreenAtDisplay(d, event->xmotion.root); if (s) groupHandleMotionEvent(s, d->pointerX, d->pointerY); break; } case ButtonPress: groupHandleButtonPressEvent(d, event); break; case ButtonRelease: groupHandleButtonReleaseEvent(d, event); break; case UnmapNotify: { CompWindow *w = findWindowAtDisplay(d, event->xunmap.window); if (!w) break; GROUP_WINDOW(w); GROUP_SCREEN(w->screen); if (w->pendingUnmaps) { if (w->shaded) { gw->windowState = WindowShaded; if (gw->group && gs->opt[GROUP_SCREEN_OPTION_SHADE_ALL].value.b) groupShadeWindows(w, gw->group, TRUE); } else if (w->minimized) { gw->windowState = WindowMinimized; if (gw->group && gs->opt[GROUP_SCREEN_OPTION_MINIMIZE_ALL].value.b) groupMinimizeWindows(w, gw->group, TRUE); } } if (gw->group) { if (gw->group->tabBar && IS_TOP_TAB(w, gw->group)) { /* on unmap of the top tab, hide the tab bar and the input prevention window */ groupTabSetVisibility(gw->group, FALSE, PERMANENT); } if (!w->pendingUnmaps) { //close event gw->ungroup = TRUE; // This will prevent setting up animations on group. groupDeleteGroupWindow(w, FALSE); gw->ungroup = FALSE; damageScreen(w->screen); } } break; } case ClientMessage: if (event->xclient.message_type == d->winActiveAtom) { CompWindow *w = findWindowAtDisplay(d, event->xclient.window); if (!w) return; GROUP_WINDOW(w); if (gw->group && gw->group->tabBar && HAS_TOP_WIN(gw->group)) { CompWindow *tw = TOP_TAB(gw->group); if (w->id != tw->id) { /* if a non top-tab has been activated, switch to the top-tab instead - but only if is visible */ if (tw->shaded) { tw->state &= ~CompWindowStateShadedMask; updateWindowAttributes(tw, FALSE); } else if (tw->minimized) unminimizeWindow(tw); if (!(tw->state & CompWindowStateHiddenMask)) { if (!gw->group->changeTab) gw->group->activateTab = gw->slot; sendWindowActivationRequest(tw->screen, tw->id); } } } } break; default: break; } UNWRAP(gd, d, handleEvent); (*d->handleEvent) (d, event); WRAP(gd, d, handleEvent, groupHandleEvent); switch (event->type) { case PropertyNotify: // focus event if (event->xproperty.atom == d->winActiveAtom) { CompWindow *w = findWindowAtDisplay(d, d->activeWindow); if (!w) break; GROUP_WINDOW(w); GROUP_SCREEN(w->screen); if (gw->group) { if (!gw->group->tabBar && !screenGrabExist(w->screen, "scale", 0) && gs->opt[GROUP_SCREEN_OPTION_RAISE].value.b) { groupRaiseWindows(w, gw->group); } if (gw->group->activateTab) { groupChangeTab(gw->group->activateTab, RotateUncertain); gw->group->activateTab = NULL; } } } break; case EnterNotify: { CompWindow *w = findWindowAtDisplay(d, event->xcrossing.window); if (!w) break; GROUP_WINDOW(w); if (gw->group) { GROUP_SCREEN(w->screen); if (gs->draggedSlot && gs->dragged && IS_TOP_TAB(w, gw->group)) { int hoverTime = gs->opt[GROUP_SCREEN_OPTION_TAB_DRAG_HOVER_TIME].value.f * 1000; if (gs->dragHoverTimeoutHandle) compRemoveTimeout(gs->dragHoverTimeoutHandle); if (hoverTime > 0) gs->dragHoverTimeoutHandle = compAddTimeout(hoverTime, groupDragHoverTimeout, w); } } } case ConfigureNotify: { CompWindow *w = findWindowAtDisplay(d, event->xconfigure.window); if (!w) break; GROUP_WINDOW(w); if (gw->group && gw->group->tabBar && IS_TOP_TAB(w, gw->group) && gw->group->inputPrevention) groupUpdateInputPreventionWindow(gw->group); } break; default: break; } } /* * groupGetOutputExtentsForWindow * */ void groupGetOutputExtentsForWindow (CompWindow * w, CompWindowExtents * output) { GROUP_SCREEN (w->screen); GROUP_WINDOW (w); UNWRAP (gs, w->screen, getOutputExtentsForWindow); (*w->screen->getOutputExtentsForWindow) (w, output); WRAP (gs, w->screen, getOutputExtentsForWindow, groupGetOutputExtentsForWindow); if (gw->group && gw->group->nWins > 1) { int glowSize = gs->opt[GROUP_SCREEN_OPTION_GLOW_SIZE].value.i; if (glowSize > output->left) output->left = glowSize; if (glowSize > output->right) output->right = glowSize; if (glowSize > output->top) output->top = glowSize; if (glowSize > output->bottom) output->bottom = glowSize; } } /* * groupWindowResizeNotify * */ void groupWindowResizeNotify(CompWindow * w, int dx, int dy, int dwidth, int dheight, Bool preview) { GROUP_SCREEN(w->screen); GROUP_DISPLAY(w->screen->display); GROUP_WINDOW(w); UNWRAP(gs, w->screen, windowResizeNotify); (*w->screen->windowResizeNotify) (w, dx, dy, dwidth, dheight, preview); WRAP(gs, w->screen, windowResizeNotify, groupWindowResizeNotify); if (gw->glowQuads) groupComputeGlowQuads (w, &gs->glowTexture.matrix); if (!dx && !dy && !dwidth && !dheight) return; if (preview) return; if (gw->group && gw->group->tabBar && (gw->group->tabBar->state != PaintOff) && IS_TOP_TAB(w, gw->group)) { groupRecalcTabBarPos(gw->group, w->screen->display->pointerX, WIN_X(w), WIN_X(w) + WIN_WIDTH(w)); } if (gw->group && !gd->ignoreMode) { if (((gw->lastState & MAXIMIZE_STATE) != (w->state & MAXIMIZE_STATE)) && gs->opt[GROUP_SCREEN_OPTION_RESIZE_UNMAXIMIZE].value.b) { int i; for (i = 0; i < gw->group->nWins; i++) { CompWindow *cw = gw->group->windows[i]; if (!cw) continue; if (cw->id == w->id) continue; maximizeWindow(cw, w->state & MAXIMIZE_STATE); } } else if ((gw->group->grabWindow == w->id) && gs->opt[GROUP_SCREEN_OPTION_RESIZE].value.b) { int i; for (i = 0; i < gw->group->nWins; i++) { CompWindow *cw = gw->group->windows[i]; if (!cw) continue; if (cw->state & MAXIMIZE_STATE) continue; if (cw->id == w->id) continue; int nx = 0; int ny = 0; if (gs->opt[GROUP_SCREEN_OPTION_RELATIVE_DISTANCE].value.b) { int distX = cw->serverX - (w->serverX - dx); int distY = cw->serverY - (w->serverY - dy); int ndistX = distX * ((float) w->serverWidth / (float) (w->serverWidth - dwidth)); int ndistY = distY * ((float) w->serverHeight / (float) (w->serverHeight - dheight)); nx = w->serverX + ndistX; ny = w->serverY + ndistY; } else { nx = cw->serverX + dx; ny = cw->serverY + dy; } int nwidth = cw->serverWidth + dwidth; int nheight = cw->serverHeight + dheight; constrainNewWindowSize(cw, nwidth, nheight, &nwidth, &nheight); XWindowChanges xwc; xwc.x = nx; xwc.y = ny; xwc.width = nwidth; xwc.height = nheight; configureXWindow(cw, CWX | CWY | CWWidth | CWHeight, &xwc); } } } } /* * groupWindowMoveNotify * */ void groupWindowMoveNotify(CompWindow * w, int dx, int dy, Bool immediate) { GROUP_SCREEN(w->screen); GROUP_DISPLAY(w->screen->display); GROUP_WINDOW(w); UNWRAP(gs, w->screen, windowMoveNotify); (*w->screen->windowMoveNotify) (w, dx, dy, immediate); WRAP(gs, w->screen, windowMoveNotify, groupWindowMoveNotify); if (gw->glowQuads) groupComputeGlowQuads (w, &gs->glowTexture.matrix); if(!gw->group || gs->queued) return; if (w->state & CompWindowStateOffscreenMask) return; Bool viewportChange = screenGrabExist (w->screen, "rotate", 0) && ((dx && !(dx % w->screen->width)) || (dy && !(dy % w->screen->height))); if (viewportChange && (gw->animateState & IS_ANIMATED)) { gw->destination.x += dx; gw->destination.y += dy; } if (gw->group->tabBar && IS_TOP_TAB(w, gw->group)) { GroupTabBarSlot *slot; GroupTabBar *bar = gw->group->tabBar; if(gs->opt[GROUP_SCREEN_OPTION_SPRING_MODEL_ON_MOVE].value.b) XOffsetRegion (bar->region, 0, dy); else XOffsetRegion (bar->region, dx, dy); bar->rightSpringX += dx; bar->leftSpringX += dx; for(slot = bar->slots; slot; slot = slot->next) { if(gs->opt[GROUP_SCREEN_OPTION_SPRING_MODEL_ON_MOVE].value.b) XOffsetRegion (slot->region, 0, dy); else XOffsetRegion (slot->region, dx, dy); slot->springX += dx; } groupUpdateInputPreventionWindow(gw->group); return; } if (gw->group->doTabbing || gd->ignoreMode || (gw->group->grabWindow != w->id) || !screenGrabExist (w->screen, "move", 0) || !gs->opt[GROUP_SCREEN_OPTION_MOVE].value.b) return; int i; for (i = 0; i < gw->group->nWins; i++) { CompWindow *cw = gw->group->windows[i]; if (!cw) continue; if (cw->id == w->id) continue; GROUP_WINDOW(cw); if (cw->state & MAXIMIZE_STATE) { if (viewportChange) { gw->needsPosSync = TRUE; groupEnqueueMoveNotify (cw, -dx, -dy, TRUE); } } else if (!viewportChange) { gw->needsPosSync = TRUE; groupEnqueueMoveNotify (cw, dx, dy, FALSE); } } } void groupWindowGrabNotify(CompWindow * w, int x, int y, unsigned int state, unsigned int mask) { GROUP_SCREEN(w->screen); GROUP_DISPLAY(w->screen->display); GROUP_WINDOW(w); if (gw->group && !gd->ignoreMode && !gs->queued) { int i; for (i = 0; i < gw->group->nWins; i++) { CompWindow *cw = gw->group->windows[i]; if (!cw) continue; if (cw->id != w->id) groupEnqueueGrabNotify (cw, x, y, state, mask); } gw->group->grabWindow = w->id; gw->group->grabMask = mask; } UNWRAP(gs, w->screen, windowGrabNotify); (*w->screen->windowGrabNotify) (w, x, y, state, mask); WRAP(gs, w->screen, windowGrabNotify, groupWindowGrabNotify); } void groupWindowUngrabNotify(CompWindow * w) { GROUP_SCREEN(w->screen); GROUP_DISPLAY(w->screen->display); GROUP_WINDOW(w); if (gw->group && !gd->ignoreMode && !gs->queued) { int i; groupDequeueMoveNotifies(w->screen); for (i = 0; i < gw->group->nWins; i++) { CompWindow *cw = gw->group->windows[i]; if (!cw) continue; if (cw->id != w->id) { GROUP_WINDOW(cw); if (gw->needsPosSync) { syncWindowPosition(cw); gw->needsPosSync = FALSE; } groupEnqueueUngrabNotify (cw); } } gw->group->grabWindow = None; gw->group->grabMask = 0; } UNWRAP(gs, w->screen, windowUngrabNotify); (*w->screen->windowUngrabNotify) (w); WRAP(gs, w->screen, windowUngrabNotify, groupWindowUngrabNotify); } Bool groupDamageWindowRect(CompWindow * w, Bool initial, BoxPtr rect) { GROUP_SCREEN(w->screen); Bool status; UNWRAP(gs,w->screen,damageWindowRect); status = (*w->screen->damageWindowRect) (w,initial,rect); WRAP(gs,w->screen,damageWindowRect,groupDamageWindowRect); if (initial) { GROUP_WINDOW(w); if (gs->opt[GROUP_SCREEN_OPTION_AUTOTAB].value.b && (w->type & gs->wMask)) { if (!gw->group && (gw->windowState == WindowNormal)) { groupAddWindowToGroup(w, NULL); groupTabGroup(w); } } if ((gw->windowState == WindowMinimized) && gw->group) { if (gs->opt[GROUP_SCREEN_OPTION_MINIMIZE_ALL].value.b) groupMinimizeWindows(w, gw->group, FALSE); } else if ((gw->windowState == WindowShaded) && gw->group) { if (gs->opt[GROUP_SCREEN_OPTION_SHADE_ALL].value.b) groupShadeWindows(w, gw->group, FALSE); } gw->windowState = WindowNormal; } return status; } void groupWindowStateChangeNotify(CompWindow *w) { GROUP_SCREEN(w->screen); GROUP_WINDOW(w); gw->lastState = w->lastState; UNWRAP(gs, w->screen, windowStateChangeNotify); (*w->screen->windowStateChangeNotify) (w); WRAP(gs, w->screen, windowStateChangeNotify, groupWindowStateChangeNotify); }