1 /**
2  *
3  * Compiz group plugin
4  *
5  * group.c
6  *
7  * Copyright : (C) 2006-2007 by Patrick Niklaus, Roi Cohen, Danny Baumann
8  * Authors: Patrick Niklaus <patrick.niklaus@googlemail.com>
9  *          Roi Cohen       <roico.beryl@gmail.com>
10  *          Danny Baumann   <maniac@opencompositing.org>
11  *
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  **/
24 
25 #include "group-internal.h"
26 
27 /*
28  * groupIsGroupWindow
29  *
30  */
31 Bool
groupIsGroupWindow(CompWindow * w)32 groupIsGroupWindow (CompWindow *w)
33 {
34     if (w->attrib.override_redirect)
35 	return FALSE;
36 
37     if (w->type & CompWindowTypeDesktopMask)
38 	return FALSE;
39 
40     if (w->invisible)
41 	return FALSE;
42 
43     if (!matchEval (groupGetWindowMatch (w->screen), w))
44 	return FALSE;
45 
46     return TRUE;
47 }
48 
49 /*
50  * groupDragHoverTimeout
51  *
52  * Description:
53  * Activates a window after a certain time a slot has been dragged over it.
54  *
55  */
56 static Bool
groupDragHoverTimeout(void * closure)57 groupDragHoverTimeout (void* closure)
58 {
59     CompWindow *w = (CompWindow *) closure;
60     if (!w)
61 	return FALSE;
62 
63     GROUP_SCREEN (w->screen);
64     GROUP_WINDOW (w);
65 
66     if (groupGetBarAnimations (w->screen))
67     {
68 	GroupTabBar *bar = gw->group->tabBar;
69 
70 	bar->bgAnimation = AnimationPulse;
71 	bar->bgAnimationTime = groupGetPulseTime (w->screen) * 1000;
72     }
73 
74     (*w->screen->activateWindow) (w);
75     gs->dragHoverTimeoutHandle = 0;
76 
77     return FALSE;
78 }
79 
80 /*
81  * groupCheckWindowProperty
82  *
83  */
84 Bool
groupCheckWindowProperty(CompWindow * w,long int * id,Bool * tabbed,GLushort * color)85 groupCheckWindowProperty (CompWindow *w,
86 			  long int   *id,
87 			  Bool       *tabbed,
88 			  GLushort   *color)
89 {
90     Atom          type;
91     int           retval, fmt;
92     unsigned long nitems, exbyte;
93     long int      *data;
94 
95     GROUP_DISPLAY (w->screen->display);
96 
97     retval = XGetWindowProperty (w->screen->display->display, w->id,
98 	     			 gd->groupWinPropertyAtom, 0, 5, False,
99 	     			 XA_CARDINAL, &type, &fmt, &nitems, &exbyte,
100 	     			 (unsigned char **)&data);
101 
102     if (retval == Success)
103     {
104 	if (type == XA_CARDINAL && fmt == 32 && nitems == 5)
105 	{
106 	    if (id)
107 		*id = data[0];
108 	    if (tabbed)
109 		*tabbed = (Bool) data[1];
110 	    if (color) {
111 		color[0] = (GLushort) data[2];
112 		color[1] = (GLushort) data[3];
113 		color[2] = (GLushort) data[4];
114 	    }
115 
116 	    XFree (data);
117 	    return TRUE;
118 	}
119 	else if (fmt != 0)
120 	    XFree (data);
121     }
122 
123     return FALSE;
124 }
125 
126 /*
127  * groupUpdateWindowProperty
128  *
129  */
130 void
groupUpdateWindowProperty(CompWindow * w)131 groupUpdateWindowProperty (CompWindow *w)
132 {
133     CompDisplay *d = w->screen->display;
134 
135     GROUP_WINDOW (w);
136     GROUP_DISPLAY (d);
137 
138     // Do not change anything in this case
139     if (gw->readOnlyProperty)
140 	return;
141 
142     if (gw->group)
143     {
144 	long int buffer[5];
145 
146 	buffer[0] = gw->group->identifier;
147 	buffer[1] = (gw->slot) ? TRUE : FALSE;
148 
149 	/* group color RGB */
150 	buffer[2] = gw->group->color[0];
151 	buffer[3] = gw->group->color[1];
152 	buffer[4] = gw->group->color[2];
153 
154 	XChangeProperty (d->display, w->id, gd->groupWinPropertyAtom,
155 			 XA_CARDINAL, 32, PropModeReplace,
156 			 (unsigned char *) buffer, 5);
157     }
158     else
159     {
160 	XDeleteProperty (d->display, w->id, gd->groupWinPropertyAtom);
161     }
162 }
163 
164 static unsigned int
groupUpdateResizeRectangle(CompWindow * w,XRectangle * masterGeometry,Bool damage)165 groupUpdateResizeRectangle (CompWindow *w,
166 			    XRectangle *masterGeometry,
167 			    Bool       damage)
168 {
169     XRectangle   newGeometry;
170     unsigned int mask = 0;
171     int          newWidth, newHeight;
172     int          widthDiff, heightDiff;
173 
174     GROUP_WINDOW (w);
175     GROUP_DISPLAY (w->screen->display);
176 
177     if (!gw->resizeGeometry || !gd->resizeInfo)
178 	return 0;
179 
180     newGeometry.x = WIN_X (w) + (masterGeometry->x -
181 				 gd->resizeInfo->origGeometry.x);
182     newGeometry.y = WIN_Y (w) + (masterGeometry->y -
183 				 gd->resizeInfo->origGeometry.y);
184 
185     widthDiff = masterGeometry->width - gd->resizeInfo->origGeometry.width;
186     newGeometry.width = MAX (1, WIN_WIDTH (w) + widthDiff);
187     heightDiff = masterGeometry->height - gd->resizeInfo->origGeometry.height;
188     newGeometry.height = MAX (1, WIN_HEIGHT (w) + heightDiff);
189 
190     if (constrainNewWindowSize (w,
191 				newGeometry.width, newGeometry.height,
192 				&newWidth, &newHeight))
193     {
194 
195 	newGeometry.width  = newWidth;
196 	newGeometry.height = newHeight;
197     }
198 
199     if (damage)
200     {
201 	if (memcmp (&newGeometry, gw->resizeGeometry,
202 		    sizeof (newGeometry)) != 0)
203 	{
204 	    addWindowDamage (w);
205 	}
206     }
207 
208     if (newGeometry.x != gw->resizeGeometry->x)
209     {
210 	gw->resizeGeometry->x = newGeometry.x;
211 	mask |= CWX;
212     }
213     if (newGeometry.y != gw->resizeGeometry->y)
214     {
215 	gw->resizeGeometry->y = newGeometry.y;
216 	mask |= CWY;
217     }
218     if (newGeometry.width != gw->resizeGeometry->width)
219     {
220 	gw->resizeGeometry->width = newGeometry.width;
221 	mask |= CWWidth;
222     }
223     if (newGeometry.height != gw->resizeGeometry->height)
224     {
225 	gw->resizeGeometry->height = newGeometry.height;
226 	mask |= CWHeight;
227     }
228 
229     return mask;
230 }
231 
232 /*
233  * groupGrabScreen
234  *
235  */
236 void
groupGrabScreen(CompScreen * s,GroupScreenGrabState newState)237 groupGrabScreen (CompScreen           *s,
238 		 GroupScreenGrabState newState)
239 {
240     GROUP_SCREEN (s);
241 
242     if ((gs->grabState != newState) && gs->grabIndex)
243     {
244 	removeScreenGrab (s, gs->grabIndex, NULL);
245 	gs->grabIndex = 0;
246     }
247 
248     if (newState == ScreenGrabSelect)
249     {
250 	gs->grabIndex = pushScreenGrab (s, None, "group");
251     }
252     else if (newState == ScreenGrabTabDrag)
253     {
254 	gs->grabIndex = pushScreenGrab (s, None, "group-drag");
255     }
256 
257     gs->grabState = newState;
258 }
259 
260 /*
261  * groupRaiseWindows
262  *
263  */
264 static void
groupRaiseWindows(CompWindow * top,GroupSelection * group)265 groupRaiseWindows (CompWindow     *top,
266 		   GroupSelection *group)
267 {
268     CompWindow **stack;
269     CompWindow *w;
270     int        count = 0, i;
271 
272     if (group->nWins == 1)
273 	return;
274 
275     stack = malloc ((group->nWins - 1) * sizeof (CompWindow *));
276     if (!stack)
277 	return;
278 
279     for (w = group->screen->windows; w; w = w->next)
280     {
281 	GROUP_WINDOW (w);
282 	if ((w->id != top->id) && (gw->group == group))
283 	    stack[count++] = w;
284     }
285 
286     for (i = 0; i < count; i++)
287 	restackWindowBelow (stack[i], top);
288 
289     free (stack);
290 }
291 
292 /*
293  * groupMinimizeWindows
294  *
295  */
296 static void
groupMinimizeWindows(CompWindow * top,GroupSelection * group,Bool minimize)297 groupMinimizeWindows (CompWindow     *top,
298 		      GroupSelection *group,
299 		      Bool           minimize)
300 {
301     int i;
302     for (i = 0; i < group->nWins; i++)
303     {
304 	CompWindow *w = group->windows[i];
305 	if (w->id == top->id)
306 	    continue;
307 
308 	if (minimize)
309 	    minimizeWindow (w);
310 	else
311 	    unminimizeWindow (w);
312     }
313 }
314 
315 /*
316  * groupShadeWindows
317  *
318  */
319 static void
groupShadeWindows(CompWindow * top,GroupSelection * group,Bool shade)320 groupShadeWindows (CompWindow     *top,
321 		   GroupSelection *group,
322 		   Bool           shade)
323 {
324     int i;
325     unsigned int state;
326 
327     for (i = 0; i < group->nWins; i++)
328     {
329 	CompWindow *w = group->windows[i];
330 	if (w->id == top->id)
331 	    continue;
332 
333 	if (shade)
334 	    state = w->state | CompWindowStateShadedMask;
335 	else
336 	    state = w->state & ~CompWindowStateShadedMask;
337 
338 	changeWindowState (w, state);
339 	updateWindowAttributes (w, CompStackingUpdateModeNone);
340     }
341 }
342 
343 /*
344  * groupDeleteGroupWindow
345  *
346  */
347 void
groupDeleteGroupWindow(CompWindow * w)348 groupDeleteGroupWindow (CompWindow *w)
349 {
350     GroupSelection *group;
351 
352     GROUP_WINDOW (w);
353     GROUP_SCREEN (w->screen);
354 
355     if (!gw->group)
356 	return;
357 
358     group = gw->group;
359 
360     if (group->tabBar && gw->slot)
361     {
362 	if (gs->draggedSlot && gs->dragged &&
363 	    gs->draggedSlot->window->id == w->id)
364 	{
365 	    groupUnhookTabBarSlot (group->tabBar, gw->slot, FALSE);
366 	}
367 	else
368 	    groupDeleteTabBarSlot (group->tabBar, gw->slot);
369     }
370 
371     if (group->nWins && group->windows)
372     {
373 	CompWindow **buf = group->windows;
374 
375 	if (group->nWins > 1)
376 	{
377 	    int counter = 0;
378 	    int i;
379 
380 	    group->windows = calloc (group->nWins - 1, sizeof(CompWindow *));
381 
382 	    for (i = 0; i < group->nWins; i++)
383 	    {
384 		if (buf[i]->id == w->id)
385 		    continue;
386 		group->windows[counter++] = buf[i];
387 	    }
388 	    group->nWins = counter;
389 
390 	    if (group->nWins == 1)
391 	    {
392 		/* Glow was removed from this window, too */
393 		damageWindowOutputExtents (group->windows[0]);
394 		updateWindowOutputExtents (group->windows[0]);
395 
396 		if (groupGetAutoUngroup (w->screen))
397 		{
398 		    if (group->changeState != NoTabChange)
399 		    {
400 			/* a change animation is pending: this most
401 			   likely means that a window must be moved
402 			   back onscreen, so we do that here */
403 			CompWindow *lw = group->windows[0];
404 
405 			groupSetWindowVisibility (lw, TRUE);
406 		    }
407 		    if (!groupGetAutotabCreate (w->screen))
408 			groupDeleteGroup (group);
409 		}
410 	    }
411 	}
412 	else
413 	{
414 	    group->windows = NULL;
415 	    groupDeleteGroup (group);
416 	}
417 
418 	free (buf);
419 
420 	damageWindowOutputExtents (w);
421 	gw->group = NULL;
422 	updateWindowOutputExtents (w);
423 	groupUpdateWindowProperty (w);
424     }
425 }
426 
427 void
groupRemoveWindowFromGroup(CompWindow * w)428 groupRemoveWindowFromGroup (CompWindow *w)
429 {
430     GROUP_WINDOW (w);
431 
432     if (!gw->group)
433 	return;
434 
435     if (gw->group->tabBar && !(gw->animateState & IS_UNGROUPING) &&
436 	(gw->group->nWins > 1))
437     {
438 	GroupSelection *group = gw->group;
439 
440 	/* if the group is tabbed, setup untabbing animation. The
441 	   window will be deleted from the group at the
442 	   end of the untabbing. */
443 	if (HAS_TOP_WIN (group))
444 	{
445 	    CompWindow *tw = TOP_TAB (group);
446 	    int        oldX = gw->orgPos.x;
447 	    int        oldY = gw->orgPos.y;
448 
449 	    gw->orgPos.x = WIN_CENTER_X (tw) - (WIN_WIDTH (w) / 2);
450 	    gw->orgPos.y = WIN_CENTER_Y (tw) - (WIN_HEIGHT (w) / 2);
451 
452 	    gw->destination.x = gw->orgPos.x + gw->mainTabOffset.x;
453 	    gw->destination.y = gw->orgPos.y + gw->mainTabOffset.y;
454 
455 	    gw->mainTabOffset.x = oldX;
456 	    gw->mainTabOffset.y = oldY;
457 
458 	    if (gw->tx || gw->ty)
459 	    {
460 		gw->tx -= (gw->orgPos.x - oldX);
461 		gw->ty -= (gw->orgPos.y - oldY);
462 	    }
463 
464 	    gw->animateState = IS_ANIMATED;
465 	    gw->xVelocity = gw->yVelocity = 0.0f;
466 	}
467 
468 	/* Although when there is no top-tab, it will never really
469 	   animate anything, if we don't start the animation,
470 	   the window will never get removed. */
471 	groupStartTabbingAnimation (group, FALSE);
472 
473 	groupSetWindowVisibility (w, TRUE);
474 	group->ungroupState = UngroupSingle;
475 	gw->animateState |= IS_UNGROUPING;
476     }
477     else
478     {
479 	/* no tab bar - delete immediately */
480 	groupDeleteGroupWindow (w);
481 
482 	if (groupGetAutotabCreate (w->screen) && groupIsGroupWindow (w))
483 	{
484 	    groupAddWindowToGroup (w, NULL, 0);
485 	    groupTabGroup (w);
486 	}
487     }
488 }
489 
490 /*
491  * groupDeleteGroup
492  *
493  */
494 void
groupDeleteGroup(GroupSelection * group)495 groupDeleteGroup (GroupSelection *group)
496 {
497     GroupSelection *next, *prev;
498     CompScreen     *s = group->screen;
499 
500     GROUP_SCREEN (s);
501     GROUP_DISPLAY (s->display);
502 
503     if (group->windows)
504     {
505 	int i;
506 
507 	if (group->tabBar)
508 	{
509 	    /* set up untabbing animation and delete the group
510 	       at the end of the animation */
511 	    groupUntabGroup (group);
512 	    group->ungroupState = UngroupAll;
513 	    return;
514 	}
515 
516 	for (i = 0; i < group->nWins; i++)
517 	{
518 	    CompWindow *cw = group->windows[i];
519 	    GROUP_WINDOW (cw);
520 
521 	    damageWindowOutputExtents (cw);
522 	    gw->group = NULL;
523 	    updateWindowOutputExtents (cw);
524 	    groupUpdateWindowProperty (cw);
525 
526 	    if (groupGetAutotabCreate (s) && groupIsGroupWindow (cw))
527 	    {
528 		groupAddWindowToGroup (cw, NULL, 0);
529 		groupTabGroup (cw);
530 	    }
531 	}
532 
533 	free (group->windows);
534 	group->windows = NULL;
535     }
536     else if (group->tabBar)
537 	groupDeleteTabBar (group);
538 
539     prev = group->prev;
540     next = group->next;
541 
542     /* relink stack */
543     if (prev || next)
544     {
545 	if (prev)
546 	{
547 	    if (next)
548 		prev->next = next;
549 	    else
550 		prev->next = NULL;
551 	}
552 	if (next)
553 	{
554 	    if (prev)
555 		next->prev = prev;
556 	    else
557 	    {
558 		next->prev = NULL;
559 		gs->groups = next;
560 	    }
561 	}
562     }
563     else
564 	gs->groups = NULL;
565 
566     if (group == gs->lastHoveredGroup)
567 	gs->lastHoveredGroup = NULL;
568     if (group == gd->lastRestackedGroup)
569 	gd->lastRestackedGroup = NULL;
570 
571     free (group);
572 }
573 
574 /*
575  * groupAddWindowToGroup
576  *
577  */
578 void
groupAddWindowToGroup(CompWindow * w,GroupSelection * group,long int initialIdent)579 groupAddWindowToGroup (CompWindow     *w,
580 		       GroupSelection *group,
581 		       long int       initialIdent)
582 {
583     GROUP_SCREEN (w->screen);
584     GROUP_WINDOW (w);
585 
586     if (gw->group)
587 	return;
588 
589     if (group)
590     {
591 	CompWindow *topTab = NULL;
592 
593 	group->windows = realloc (group->windows,
594 				  sizeof (CompWindow *) * (group->nWins + 1));
595 	group->windows[group->nWins] = w;
596 	group->nWins++;
597 	gw->group = group;
598 
599 	updateWindowOutputExtents (w);
600 	groupUpdateWindowProperty (w);
601 
602 	if (group->nWins == 2)
603 	{
604 	    /* first window in the group got its glow, too */
605 	    updateWindowOutputExtents (group->windows[0]);
606 	}
607 
608 	if (group->tabBar)
609 	{
610 	    if (HAS_TOP_WIN (group))
611 		topTab = TOP_TAB (group);
612 	    else if (HAS_PREV_TOP_WIN (group))
613 	    {
614 		topTab = PREV_TOP_TAB (group);
615 		group->topTab = group->prevTopTab;
616 		group->prevTopTab = NULL;
617 	    }
618 
619 	    if (topTab)
620 	    {
621 		if (!gw->slot)
622 		    groupCreateSlot (group, w);
623 
624 		gw->destination.x = WIN_CENTER_X (topTab) - (WIN_WIDTH (w) / 2);
625 		gw->destination.y = WIN_CENTER_Y (topTab) -
626 		                    (WIN_HEIGHT (w) / 2);
627 		gw->mainTabOffset.x = WIN_X (w) - gw->destination.x;
628 		gw->mainTabOffset.y = WIN_Y (w) - gw->destination.y;
629 		gw->orgPos.x = WIN_X (w);
630 		gw->orgPos.y = WIN_Y (w);
631 
632 		gw->xVelocity = gw->yVelocity = 0.0f;
633 
634 		gw->animateState = IS_ANIMATED;
635 
636 		groupStartTabbingAnimation (group, TRUE);
637 
638 		addWindowDamage (w);
639 	    }
640 	}
641     }
642     else
643     {
644 	/* create new group */
645 	GroupSelection *g = malloc (sizeof (GroupSelection));
646 	if (!g)
647 	    return;
648 
649 	g->windows = malloc (sizeof (CompWindow *));
650 	if (!g->windows)
651 	{
652 	    free (g);
653 	    return;
654 	}
655 
656 	g->windows[0] = w;
657 	g->screen     = w->screen;
658 	g->nWins      = 1;
659 
660 	g->topTab      = NULL;
661 	g->prevTopTab  = NULL;
662 	g->nextTopTab  = NULL;
663 
664 	g->changeAnimationTime      = 0;
665 	g->changeAnimationDirection = 0;
666 
667 	g->changeState  = NoTabChange;
668 	g->tabbingState = NoTabbing;
669 	g->ungroupState = UngroupNone;
670 
671 	g->tabBar = NULL;
672 
673 	g->checkFocusAfterTabChange = FALSE;
674 
675 	g->grabWindow = None;
676 	g->grabMask   = 0;
677 
678 	g->inputPrevention = None;
679 	g->ipwMapped       = FALSE;
680 
681 	/* glow color */
682 	g->color[0] = (int)(rand () / (((double)RAND_MAX + 1) / 0xffff));
683 	g->color[1] = (int)(rand () / (((double)RAND_MAX + 1) / 0xffff));
684 	g->color[2] = (int)(rand () / (((double)RAND_MAX + 1) / 0xffff));
685 	g->color[3] = 0xffff;
686 
687 	if (initialIdent)
688 	    g->identifier = initialIdent;
689 	else
690 	{
691 	    /* we got no valid group Id passed, so find out a new valid
692 	       unique one */
693 	    GroupSelection *tg;
694 	    Bool           invalidID = FALSE;
695 
696 	    g->identifier = gs->groups ? gs->groups->identifier : 0;
697 	    do
698 	    {
699 		invalidID = FALSE;
700 		for (tg = gs->groups; tg; tg = tg->next)
701 		{
702 		    if (tg->identifier == g->identifier)
703 		    {
704 			invalidID = TRUE;
705 
706 			g->identifier++;
707 			break;
708 		    }
709 		}
710 	    }
711 	    while (invalidID);
712 	}
713 
714 	/* relink stack */
715 	if (gs->groups)
716 	    gs->groups->prev = g;
717 
718 	g->next = gs->groups;
719 	g->prev = NULL;
720 	gs->groups = g;
721 
722 	gw->group = g;
723 
724 	groupUpdateWindowProperty (w);
725     }
726 }
727 
728 /*
729  * groupGroupWindows
730  *
731  */
732 Bool
groupGroupWindows(CompDisplay * d,CompAction * action,CompActionState state,CompOption * option,int nOption)733 groupGroupWindows (CompDisplay     *d,
734 		   CompAction      *action,
735 		   CompActionState state,
736 		   CompOption      *option,
737 		   int             nOption)
738 {
739     CompScreen *s;
740     Window     xid;
741 
742     xid = getIntOptionNamed (option, nOption, "root", 0);
743     s = findScreenAtDisplay (d, xid);
744 
745     if (s)
746     {
747 	GROUP_SCREEN (s);
748 
749 	if (gs->tmpSel.nWins > 0)
750 	{
751 	    int            i;
752 	    CompWindow     *cw;
753 	    GroupSelection *group = NULL;
754 	    Bool           tabbed = FALSE;
755 
756 	    for (i = 0; i < gs->tmpSel.nWins; i++)
757 	    {
758 		cw = gs->tmpSel.windows[i];
759 		GROUP_WINDOW (cw);
760 
761 		if (gw->group)
762 		{
763 		    if (!tabbed || group->tabBar)
764 			group = gw->group;
765 
766 		    if (group->tabBar)
767 			tabbed = TRUE;
768 		}
769 	    }
770 
771 	    /* we need to do one first to get the pointer of a new group */
772 	    cw = gs->tmpSel.windows[0];
773 	    GROUP_WINDOW (cw);
774 
775 	    if (gw->group && (group != gw->group))
776 		groupDeleteGroupWindow (cw);
777 	    groupAddWindowToGroup (cw, group, 0);
778 	    addWindowDamage (cw);
779 
780 	    gw->inSelection = FALSE;
781 	    group = gw->group;
782 
783 	    for (i = 1; i < gs->tmpSel.nWins; i++)
784 	    {
785 		cw = gs->tmpSel.windows[i];
786 		GROUP_WINDOW (cw);
787 
788 		if (gw->group && (group != gw->group))
789 		    groupDeleteGroupWindow (cw);
790 		groupAddWindowToGroup (cw, group, 0);
791 		addWindowDamage (cw);
792 
793 		gw->inSelection = FALSE;
794 	    }
795 
796 	    /* exit selection */
797 	    free (gs->tmpSel.windows);
798 	    gs->tmpSel.windows = NULL;
799 	    gs->tmpSel.nWins = 0;
800 	}
801     }
802 
803     return FALSE;
804 }
805 
806 /*
807  * groupUnGroupWindows
808  *
809  */
810 Bool
groupUnGroupWindows(CompDisplay * d,CompAction * action,CompActionState state,CompOption * option,int nOption)811 groupUnGroupWindows (CompDisplay     *d,
812 		     CompAction      *action,
813 		     CompActionState state,
814 		     CompOption      *option,
815 		     int             nOption)
816 {
817     Window     xid;
818     CompWindow *w;
819 
820     xid = getIntOptionNamed (option, nOption, "window", 0);
821     w   = findTopLevelWindowAtDisplay (d, xid);
822     if (w)
823     {
824 	GROUP_WINDOW (w);
825 
826 	if (gw->group)
827 	    groupDeleteGroup (gw->group);
828     }
829 
830     return FALSE;
831 }
832 
833 /*
834  * groupRemoveWindow
835  *
836  */
837 Bool
groupRemoveWindow(CompDisplay * d,CompAction * action,CompActionState state,CompOption * option,int nOption)838 groupRemoveWindow (CompDisplay     *d,
839 		   CompAction      *action,
840 		   CompActionState state,
841 		   CompOption      *option,
842 		   int             nOption)
843 {
844     Window     xid;
845     CompWindow *w;
846 
847     xid = getIntOptionNamed (option, nOption, "window", 0);
848     w   = findWindowAtDisplay (d, xid);
849     if (w)
850     {
851 	GROUP_WINDOW (w);
852 
853 	if (gw->group)
854 	    groupRemoveWindowFromGroup (w);
855     }
856 
857     return FALSE;
858 }
859 
860 /*
861  * groupCloseWindows
862  *
863  */
864 Bool
groupCloseWindows(CompDisplay * d,CompAction * action,CompActionState state,CompOption * option,int nOption)865 groupCloseWindows (CompDisplay     *d,
866 		   CompAction      *action,
867 		   CompActionState state,
868 		   CompOption      *option,
869 		   int             nOption)
870 {
871     Window     xid;
872     CompWindow *w;
873 
874     xid = getIntOptionNamed (option, nOption, "window", 0);
875     w   = findWindowAtDisplay (d, xid);
876     if (w)
877     {
878 	GROUP_WINDOW (w);
879 
880 	if (gw->group)
881 	{
882 	    int i;
883 
884 	    for (i = 0; i < gw->group->nWins; i++)
885 		closeWindow (gw->group->windows[i],
886 			     getCurrentTimeFromDisplay (d));
887 	}
888     }
889 
890     return FALSE;
891 }
892 
893 /*
894  * groupChangeColor
895  *
896  */
897 Bool
groupChangeColor(CompDisplay * d,CompAction * action,CompActionState state,CompOption * option,int nOption)898 groupChangeColor (CompDisplay     *d,
899 		  CompAction      *action,
900 		  CompActionState state,
901 		  CompOption      *option,
902 		  int             nOption)
903 {
904     Window     xid;
905     CompWindow *w;
906 
907     xid = getIntOptionNamed (option, nOption, "window", 0);
908     w   = findWindowAtDisplay (d, xid);
909     if (w)
910     {
911 	GROUP_WINDOW (w);
912 
913 	if (gw->group)
914 	{
915 	    GLushort *color = gw->group->color;
916 	    float    factor = ((float)RAND_MAX + 1) / 0xffff;
917 
918 	    color[0] = (int)(rand () / factor);
919 	    color[1] = (int)(rand () / factor);
920 	    color[2] = (int)(rand () / factor);
921 
922 	    groupRenderTopTabHighlight (gw->group);
923 	    damageScreen (w->screen);
924 	}
925     }
926 
927     return FALSE;
928 }
929 
930 /*
931  * groupSetIgnore
932  *
933  */
934 Bool
groupSetIgnore(CompDisplay * d,CompAction * action,CompActionState state,CompOption * option,int nOption)935 groupSetIgnore (CompDisplay     *d,
936 		CompAction      *action,
937 		CompActionState state,
938 		CompOption      *option,
939 		int             nOption)
940 {
941     GROUP_DISPLAY (d);
942 
943     gd->ignoreMode = TRUE;
944 
945     if (state & CompActionStateInitKey)
946 	action->state |= CompActionStateTermKey;
947 
948     return FALSE;
949 }
950 
951 /*
952  * groupUnsetIgnore
953  *
954  */
955 Bool
groupUnsetIgnore(CompDisplay * d,CompAction * action,CompActionState state,CompOption * option,int nOption)956 groupUnsetIgnore (CompDisplay     *d,
957 		  CompAction      *action,
958 		  CompActionState state,
959 		  CompOption      *option,
960 		  int             nOption)
961 {
962     GROUP_DISPLAY (d);
963 
964     gd->ignoreMode = FALSE;
965 
966     action->state &= ~CompActionStateTermKey;
967 
968     return FALSE;
969 }
970 
971 /*
972  * groupHandleButtonPressEvent
973  *
974  */
975 static void
groupHandleButtonPressEvent(CompScreen * s,XEvent * event)976 groupHandleButtonPressEvent (CompScreen *s,
977 			     XEvent     *event)
978 {
979     GroupSelection *group;
980     int            xRoot, yRoot, button;
981 
982     GROUP_SCREEN (s);
983 
984     xRoot  = event->xbutton.x_root;
985     yRoot  = event->xbutton.y_root;
986     button = event->xbutton.button;
987 
988     for (group = gs->groups; group; group = group->next)
989     {
990 	if (group->inputPrevention != event->xbutton.window)
991 	    continue;
992 
993 	if (!group->tabBar)
994 	    continue;
995 
996 	switch (button) {
997 	case Button1:
998 	    {
999 		GroupTabBarSlot *slot;
1000 
1001 		for (slot = group->tabBar->slots; slot; slot = slot->next)
1002 		{
1003 		    if (XPointInRegion (slot->region, xRoot, yRoot))
1004 		    {
1005 			gs->draggedSlot = slot;
1006 			/* The slot isn't dragged yet */
1007 			gs->dragged = FALSE;
1008 			gs->prevX = xRoot;
1009 			gs->prevY = yRoot;
1010 
1011 			if (!otherScreenGrabExist(s, "group", "group-drag", NULL))
1012 			    groupGrabScreen (s, ScreenGrabTabDrag);
1013 		    }
1014 		}
1015 	    }
1016 	    break;
1017 
1018 	case Button4:
1019 	case Button5:
1020 	    {
1021 		CompWindow  *topTab = NULL;
1022 		GroupWindow *gw;
1023 
1024 		if (group->nextTopTab)
1025 		    topTab = NEXT_TOP_TAB (group);
1026 		else if (group->topTab)
1027 		{
1028 		    /* If there are no tabbing animations,
1029 		       topTab is never NULL. */
1030 		    topTab = TOP_TAB (group);
1031 		}
1032 
1033 		if (!topTab)
1034 		    return;
1035 
1036 		gw = GET_GROUP_WINDOW (topTab, gs);
1037 
1038 		if (button == Button4)
1039 		{
1040 		    if (gw->slot->prev)
1041 			groupChangeTab (gw->slot->prev, RotateLeft);
1042 		    else
1043 			groupChangeTab (gw->group->tabBar->revSlots,
1044 					RotateLeft);
1045 		}
1046 		else
1047 		{
1048 		    if (gw->slot->next)
1049 			groupChangeTab (gw->slot->next, RotateRight);
1050 		    else
1051 			groupChangeTab (gw->group->tabBar->slots, RotateRight);
1052 		}
1053 		break;
1054 	    }
1055 	}
1056 
1057 	break;
1058     }
1059 }
1060 
1061 /*
1062  * groupHandleButtonReleaseEvent
1063  *
1064  */
1065 static void
groupHandleButtonReleaseEvent(CompScreen * s,XEvent * event)1066 groupHandleButtonReleaseEvent (CompScreen *s,
1067 			       XEvent     *event)
1068 {
1069     GroupSelection *group;
1070     int            vx, vy;
1071     Region         newRegion;
1072     Bool           inserted = FALSE;
1073     Bool           wasInTabBar = FALSE;
1074 
1075     GROUP_SCREEN (s);
1076 
1077     if (event->xbutton.button != 1)
1078 	return;
1079 
1080     if (!gs->draggedSlot)
1081 	return;
1082 
1083     if (!gs->dragged)
1084     {
1085 	groupChangeTab (gs->draggedSlot, RotateUncertain);
1086 	gs->draggedSlot = NULL;
1087 
1088 	if (gs->grabState == ScreenGrabTabDrag)
1089 	    groupGrabScreen (s, ScreenGrabNone);
1090 
1091 	return;
1092     }
1093 
1094     GROUP_WINDOW (gs->draggedSlot->window);
1095 
1096     newRegion = XCreateRegion ();
1097     if (!newRegion)
1098 	return;
1099 
1100     XUnionRegion (newRegion, gs->draggedSlot->region, newRegion);
1101 
1102     groupGetDrawOffsetForSlot (gs->draggedSlot, &vx, &vy);
1103     XOffsetRegion (newRegion, vx, vy);
1104 
1105     for (group = gs->groups; group; group = group->next)
1106     {
1107 	Bool            inTabBar;
1108 	Region          clip, buf;
1109 	GroupTabBarSlot *slot;
1110 
1111 	if (!group->tabBar || !HAS_TOP_WIN (group))
1112 	    continue;
1113 
1114 	/* create clipping region */
1115 	clip = groupGetClippingRegion (TOP_TAB (group));
1116 	if (!clip)
1117 	    continue;
1118 
1119 	buf = XCreateRegion ();
1120 	if (!buf)
1121 	{
1122 	    XDestroyRegion (clip);
1123 	    continue;
1124 	}
1125 
1126 	XIntersectRegion (newRegion, group->tabBar->region, buf);
1127 	XSubtractRegion (buf, clip, buf);
1128 	XDestroyRegion (clip);
1129 
1130 	inTabBar = !XEmptyRegion (buf);
1131 	XDestroyRegion (buf);
1132 
1133 	if (!inTabBar)
1134 	    continue;
1135 
1136 	wasInTabBar = TRUE;
1137 
1138 	for (slot = group->tabBar->slots; slot; slot = slot->next)
1139 	{
1140 	    GroupTabBarSlot *tmpDraggedSlot;
1141 	    GroupSelection  *tmpGroup;
1142 	    Region          slotRegion, buf;
1143 	    XRectangle      rect;
1144 	    Bool            inSlot;
1145 
1146 	    if (slot == gs->draggedSlot)
1147 		continue;
1148 
1149 	    slotRegion = XCreateRegion ();
1150 	    if (!slotRegion)
1151 		continue;
1152 
1153 	    if (slot->prev && slot->prev != gs->draggedSlot)
1154 	    {
1155 		rect.x = slot->prev->region->extents.x2;
1156 	    }
1157 	    else if (slot->prev && slot->prev == gs->draggedSlot &&
1158 		     gs->draggedSlot->prev)
1159 	    {
1160 		rect.x = gs->draggedSlot->prev->region->extents.x2;
1161 	    }
1162 	    else
1163 		rect.x = group->tabBar->region->extents.x1;
1164 
1165 	    rect.y = slot->region->extents.y1;
1166 
1167 	    if (slot->next && slot->next != gs->draggedSlot)
1168 	    {
1169 		rect.width = slot->next->region->extents.x1 - rect.x;
1170 	    }
1171 	    else if (slot->next && slot->next == gs->draggedSlot &&
1172 		     gs->draggedSlot->next)
1173 	    {
1174 		rect.width = gs->draggedSlot->next->region->extents.x1 - rect.x;
1175 	    }
1176 	    else
1177 		rect.width = group->tabBar->region->extents.x2;
1178 
1179 	    rect.height = slot->region->extents.y2 - slot->region->extents.y1;
1180 
1181 	    XUnionRectWithRegion (&rect, slotRegion, slotRegion);
1182 
1183 	    buf = XCreateRegion ();
1184 	    if (!buf)
1185 		continue;
1186 
1187 	    XIntersectRegion (newRegion, slotRegion, buf);
1188 	    inSlot = !XEmptyRegion (buf);
1189 
1190 	    XDestroyRegion (buf);
1191 	    XDestroyRegion (slotRegion);
1192 
1193 	    if (!inSlot)
1194 		continue;
1195 
1196 	    tmpDraggedSlot = gs->draggedSlot;
1197 
1198 	    if (group != gw->group)
1199 	    {
1200 		CompWindow     *w = gs->draggedSlot->window;
1201 		GroupSelection *tmpGroup = gw->group;
1202 		int            oldPosX = WIN_CENTER_X (w);
1203 		int            oldPosY = WIN_CENTER_Y (w);
1204 
1205 		/* if the dragged window is not the top tab,
1206 		   move it onscreen */
1207 		if (tmpGroup->topTab && !IS_TOP_TAB (w, tmpGroup))
1208 		{
1209 		    CompWindow *tw = TOP_TAB (tmpGroup);
1210 
1211 		    oldPosX = WIN_CENTER_X (tw) + gw->mainTabOffset.x;
1212 		    oldPosY = WIN_CENTER_Y (tw) + gw->mainTabOffset.y;
1213 
1214 		    groupSetWindowVisibility (w, TRUE);
1215 		}
1216 
1217 		/* Change the group. */
1218 		groupDeleteGroupWindow (gs->draggedSlot->window);
1219 		groupAddWindowToGroup (gs->draggedSlot->window, group, 0);
1220 
1221 		/* we saved the original center position in oldPosX/Y before -
1222 		   now we should apply that to the new main tab offset */
1223 		if (HAS_TOP_WIN (group))
1224 		{
1225 		    CompWindow *tw = TOP_TAB (group);
1226 		    gw->mainTabOffset.x = oldPosX - WIN_CENTER_X (tw);
1227 		    gw->mainTabOffset.y = oldPosY - WIN_CENTER_Y (tw);
1228 		}
1229 	    }
1230 	    else
1231 		groupUnhookTabBarSlot (group->tabBar, gs->draggedSlot, TRUE);
1232 
1233 	    gs->draggedSlot = NULL;
1234 	    gs->dragged = FALSE;
1235 	    inserted = TRUE;
1236 
1237 	    if ((tmpDraggedSlot->region->extents.x1 +
1238 		 tmpDraggedSlot->region->extents.x2 + (2 * vx)) / 2 >
1239 		(slot->region->extents.x1 + slot->region->extents.x2) / 2)
1240 	    {
1241 		groupInsertTabBarSlotAfter (group->tabBar,
1242 					    tmpDraggedSlot, slot);
1243 	    }
1244 	    else
1245 		groupInsertTabBarSlotBefore (group->tabBar,
1246 					     tmpDraggedSlot, slot);
1247 
1248 	    groupDamageTabBarRegion (group);
1249 
1250 	    /* Hide tab-bars. */
1251 	    for (tmpGroup = gs->groups; tmpGroup; tmpGroup = tmpGroup->next)
1252 	    {
1253 		if (group == tmpGroup)
1254 		    groupTabSetVisibility (tmpGroup, TRUE, 0);
1255 		else
1256 		    groupTabSetVisibility (tmpGroup, FALSE, PERMANENT);
1257 	    }
1258 
1259 	    break;
1260 	}
1261 
1262 	if (inserted)
1263 	    break;
1264     }
1265 
1266     XDestroyRegion (newRegion);
1267 
1268     if (!inserted)
1269     {
1270 	CompWindow     *draggedSlotWindow = gs->draggedSlot->window;
1271 	GroupSelection *tmpGroup;
1272 
1273 	for (tmpGroup = gs->groups; tmpGroup; tmpGroup = tmpGroup->next)
1274 	    groupTabSetVisibility (tmpGroup, FALSE, PERMANENT);
1275 
1276 	gs->draggedSlot = NULL;
1277 	gs->dragged = FALSE;
1278 
1279 	if (groupGetDndUngroupWindow (s) && !wasInTabBar)
1280 	{
1281 	    groupRemoveWindowFromGroup (draggedSlotWindow);
1282 	}
1283 	else if (gw->group && gw->group->topTab)
1284 	{
1285 	    groupRecalcTabBarPos (gw->group,
1286 				  (gw->group->tabBar->region->extents.x1 +
1287 				   gw->group->tabBar->region->extents.x2) / 2,
1288 				  gw->group->tabBar->region->extents.x1,
1289 				  gw->group->tabBar->region->extents.x2);
1290 	}
1291 
1292 	/* to remove the painted slot */
1293 	damageScreen (s);
1294     }
1295 
1296     if (gs->grabState == ScreenGrabTabDrag)
1297 	groupGrabScreen (s, ScreenGrabNone);
1298 
1299     if (gs->dragHoverTimeoutHandle)
1300     {
1301 	compRemoveTimeout (gs->dragHoverTimeoutHandle);
1302 	gs->dragHoverTimeoutHandle = 0;
1303     }
1304 }
1305 
1306 /*
1307  * groupHandleMotionEvent
1308  *
1309  */
1310 
1311 /* the radius to determine if it was a click or a drag */
1312 #define RADIUS 5
1313 
1314 static void
groupHandleMotionEvent(CompScreen * s,int xRoot,int yRoot)1315 groupHandleMotionEvent (CompScreen *s,
1316 			int        xRoot,
1317 			int        yRoot)
1318 {
1319     GROUP_SCREEN (s);
1320 
1321     if (gs->grabState == ScreenGrabTabDrag)
1322     {
1323 	int    dx, dy;
1324 	int    vx, vy;
1325 	REGION reg;
1326 	Region draggedRegion = gs->draggedSlot->region;
1327 
1328 	reg.rects = &reg.extents;
1329 	reg.numRects = 1;
1330 
1331 	dx = xRoot - gs->prevX;
1332 	dy = yRoot - gs->prevY;
1333 
1334 	if (gs->dragged || abs (dx) > RADIUS || abs (dy) > RADIUS)
1335 	{
1336 	    gs->prevX = xRoot;
1337 	    gs->prevY = yRoot;
1338 
1339 	    if (!gs->dragged)
1340 	    {
1341 		GroupSelection *group;
1342 		BoxRec         *box;
1343 
1344 		GROUP_WINDOW (gs->draggedSlot->window);
1345 
1346 		gs->dragged = TRUE;
1347 
1348 		for (group = gs->groups; group; group = group->next)
1349 		    groupTabSetVisibility (group, TRUE, PERMANENT);
1350 
1351 		box = &gw->group->tabBar->region->extents;
1352 		groupRecalcTabBarPos (gw->group, (box->x1 + box->x2) / 2,
1353 				      box->x1, box->x2);
1354 	    }
1355 
1356 	    groupGetDrawOffsetForSlot (gs->draggedSlot, &vx, &vy);
1357 
1358 	    reg.extents.x1 = draggedRegion->extents.x1 + vx;
1359 	    reg.extents.y1 = draggedRegion->extents.y1 + vy;
1360 	    reg.extents.x2 = draggedRegion->extents.x2 + vx;
1361 	    reg.extents.y2 = draggedRegion->extents.y2 + vy;
1362 	    damageScreenRegion (s, &reg);
1363 
1364 	    XOffsetRegion (gs->draggedSlot->region, dx, dy);
1365 	    gs->draggedSlot->springX =
1366 		(gs->draggedSlot->region->extents.x1 +
1367 		 gs->draggedSlot->region->extents.x2) / 2;
1368 
1369 	    reg.extents.x1 = draggedRegion->extents.x1 + vx;
1370 	    reg.extents.y1 = draggedRegion->extents.y1 + vy;
1371 	    reg.extents.x2 = draggedRegion->extents.x2 + vx;
1372 	    reg.extents.y2 = draggedRegion->extents.y2 + vy;
1373 	    damageScreenRegion (s, &reg);
1374 	}
1375     }
1376     else if (gs->grabState == ScreenGrabSelect)
1377     {
1378 	groupDamageSelectionRect (s, xRoot, yRoot);
1379     }
1380 }
1381 
1382 /*
1383  * groupHandleEvent
1384  *
1385  */
1386 void
groupHandleEvent(CompDisplay * d,XEvent * event)1387 groupHandleEvent (CompDisplay *d,
1388 		  XEvent      *event)
1389 {
1390     CompWindow *w;
1391     CompScreen *s;
1392 
1393     GROUP_DISPLAY (d);
1394 
1395     switch (event->type) {
1396     case MotionNotify:
1397 	s = findScreenAtDisplay (d, event->xmotion.root);
1398 	if (s)
1399 	    groupHandleMotionEvent (s, pointerX, pointerY);
1400 	break;
1401 
1402     case ButtonPress:
1403 	s = findScreenAtDisplay (d, event->xbutton.root);
1404 	if (s)
1405 	    groupHandleButtonPressEvent (s, event);
1406 	break;
1407 
1408     case ButtonRelease:
1409 	s = findScreenAtDisplay (d, event->xbutton.root);
1410 	if (s)
1411 	    groupHandleButtonReleaseEvent (s, event);
1412 	break;
1413 
1414     case MapNotify:
1415 	w = findWindowAtDisplay (d, event->xmap.window);
1416 	if (w)
1417 	{
1418 	    CompWindow *cw;
1419 	    for (cw = w->screen->windows; cw; cw = cw->next)
1420 	    {
1421 		if (w->id == cw->frame)
1422 		{
1423 		    GROUP_WINDOW (cw);
1424 		    if (gw->windowHideInfo)
1425 			XUnmapWindow (cw->screen->display->display, cw->frame);
1426 		}
1427 	    }
1428 	}
1429 	break;
1430 
1431     case UnmapNotify:
1432 	w = findWindowAtDisplay (d, event->xunmap.window);
1433 	if (w)
1434 	{
1435 	    GROUP_WINDOW (w);
1436 
1437 	    if (w->pendingUnmaps)
1438 	    {
1439 		if (w->shaded)
1440 		{
1441 		    gw->windowState = WindowShaded;
1442 
1443 		    if (gw->group && groupGetShadeAll (w->screen))
1444 			groupShadeWindows (w, gw->group, TRUE);
1445 		}
1446 		else if (w->minimized)
1447 		{
1448 		    gw->windowState = WindowMinimized;
1449 
1450 		    if (gw->group && groupGetMinimizeAll (w->screen))
1451 			groupMinimizeWindows (w, gw->group, TRUE);
1452 		}
1453 	    }
1454 
1455 	    if (gw->group)
1456 	    {
1457 		if (gw->group->tabBar && IS_TOP_TAB (w, gw->group))
1458 		{
1459 		    /* on unmap of the top tab, hide the tab bar and the
1460 		       input prevention window */
1461 		    groupTabSetVisibility (gw->group, FALSE, PERMANENT);
1462 		}
1463 		if (!w->pendingUnmaps)
1464 		{
1465 		    /* close event */
1466 		    if (!(gw->animateState & IS_UNGROUPING))
1467 		    {
1468 			groupDeleteGroupWindow (w);
1469 			damageScreen (w->screen);
1470 		    }
1471 		}
1472 	    }
1473 	}
1474 	break;
1475 
1476     case ClientMessage:
1477 	if (event->xclient.message_type == d->winActiveAtom)
1478 	{
1479 	    w = findWindowAtDisplay (d, event->xclient.window);
1480 	    if (w)
1481 	    {
1482 		GROUP_WINDOW (w);
1483 
1484 		if (gw->group && gw->group->tabBar &&
1485 		    !IS_TOP_TAB (w, gw->group))
1486 		{
1487 		    gw->group->checkFocusAfterTabChange = TRUE;
1488 		    groupChangeTab (gw->slot, RotateUncertain);
1489 		}
1490 	    }
1491 	}
1492 	else if (event->xclient.message_type == gd->resizeNotifyAtom)
1493 	{
1494 	    CompWindow *w;
1495 	    w = findWindowAtDisplay (d, event->xclient.window);
1496 
1497 	    if (w && gd->resizeInfo && (w == gd->resizeInfo->resizedWindow))
1498 	    {
1499 		GROUP_WINDOW (w);
1500 		GROUP_SCREEN (w->screen);
1501 
1502 		if (gw->group)
1503 		{
1504 		    int        i;
1505 		    XRectangle rect;
1506 
1507 		    rect.x      = event->xclient.data.l[0];
1508 		    rect.y      = event->xclient.data.l[1];
1509 		    rect.width  = event->xclient.data.l[2];
1510 		    rect.height = event->xclient.data.l[3];
1511 
1512 		    for (i = 0; i < gw->group->nWins; i++)
1513 		    {
1514 			CompWindow  *cw = gw->group->windows[i];
1515 			GroupWindow *gcw;
1516 
1517 			gcw = GET_GROUP_WINDOW (cw, gs);
1518 			if (gcw->resizeGeometry)
1519 			{
1520 			    if (groupUpdateResizeRectangle (cw, &rect, TRUE))
1521 				addWindowDamage (cw);
1522 			}
1523 		    }
1524 		}
1525 	    }
1526 	}
1527 	break;
1528 
1529     default:
1530 	if (event->type == d->shapeEvent + ShapeNotify)
1531 	{
1532 	    XShapeEvent *se = (XShapeEvent *) event;
1533 	    if (se->kind == ShapeInput)
1534 	    {
1535 		CompWindow *w;
1536 		w = findWindowAtDisplay (d, se->window);
1537 		if (w)
1538 		{
1539 		    GROUP_WINDOW (w);
1540 
1541 		    if (gw->windowHideInfo)
1542 			groupClearWindowInputShape (w, gw->windowHideInfo);
1543 		}
1544 	    }
1545 	}
1546 	break;
1547     }
1548 
1549     UNWRAP (gd, d, handleEvent);
1550     (*d->handleEvent) (d, event);
1551     WRAP (gd, d, handleEvent, groupHandleEvent);
1552 
1553     switch (event->type) {
1554     case PropertyNotify:
1555 	if (event->xproperty.atom == d->wmNameAtom)
1556 	{
1557 	    CompWindow *w;
1558 	    w = findWindowAtDisplay (d, event->xproperty.window);
1559 	    if (w)
1560 	    {
1561 		GROUP_WINDOW (w);
1562 
1563 		if (gw->group && gw->group->tabBar &&
1564 		    gw->group->tabBar->textSlot    &&
1565 		    gw->group->tabBar->textSlot->window == w)
1566 		{
1567 		    /* make sure we are using the updated name */
1568 		    groupRenderWindowTitle (gw->group);
1569 		    groupDamageTabBarRegion (gw->group);
1570 		}
1571 	    }
1572 	}
1573 	break;
1574 
1575     case EnterNotify:
1576 	{
1577 	    CompWindow *w;
1578 	    w = findWindowAtDisplay (d, event->xcrossing.window);
1579 	    if (w)
1580 	    {
1581 		GROUP_WINDOW (w);
1582 		GROUP_SCREEN (w->screen);
1583 
1584 		if (gs->showDelayTimeoutHandle)
1585 		    compRemoveTimeout (gs->showDelayTimeoutHandle);
1586 
1587 		if (w->id != w->screen->grabWindow)
1588 		    groupUpdateTabBars (w->screen, w->id);
1589 
1590 		if (gw->group)
1591 		{
1592 		    if (gs->draggedSlot && gs->dragged &&
1593 			IS_TOP_TAB (w, gw->group))
1594 		    {
1595 			int hoverTime;
1596 			hoverTime = groupGetDragHoverTime (w->screen) * 1000;
1597 			if (gs->dragHoverTimeoutHandle)
1598 			    compRemoveTimeout (gs->dragHoverTimeoutHandle);
1599 
1600 			if (hoverTime > 0)
1601 			    gs->dragHoverTimeoutHandle =
1602 				compAddTimeout (hoverTime,
1603 						(float) hoverTime * 1.2,
1604 						groupDragHoverTimeout, w);
1605 		    }
1606 		}
1607 	    }
1608 	}
1609 	break;
1610 
1611     case ConfigureNotify:
1612 	{
1613 	    CompWindow *w;
1614 
1615 	    w = findWindowAtDisplay (d, event->xconfigure.window);
1616 	    if (w)
1617 	    {
1618 		GROUP_WINDOW (w);
1619 
1620 		if (gw->group && gw->group->tabBar &&
1621 		    IS_TOP_TAB (w, gw->group)      &&
1622 		    gw->group->inputPrevention && gw->group->ipwMapped)
1623 		{
1624 		    XWindowChanges xwc;
1625 
1626 		    xwc.stack_mode = Above;
1627 		    xwc.sibling = w->id;
1628 
1629 		    XConfigureWindow (w->screen->display->display,
1630 				      gw->group->inputPrevention,
1631 				      CWSibling | CWStackMode, &xwc);
1632 		}
1633 
1634 		if (event->xconfigure.above != None)
1635 		{
1636 		    if (gw->group && !gw->group->tabBar &&
1637 			(gw->group != gd->lastRestackedGroup))
1638 		    {
1639 			if (groupGetRaiseAll (w->screen))
1640 			    groupRaiseWindows (w, gw->group);
1641 		    }
1642 		    if (w->managed && !w->attrib.override_redirect)
1643 			gd->lastRestackedGroup = gw->group;
1644 		}
1645 	    }
1646 	}
1647 	break;
1648 
1649     default:
1650 	break;
1651     }
1652 }
1653 
1654 /*
1655  * groupGetOutputExtentsForWindow
1656  *
1657  */
1658 void
groupGetOutputExtentsForWindow(CompWindow * w,CompWindowExtents * output)1659 groupGetOutputExtentsForWindow (CompWindow        *w,
1660 				CompWindowExtents *output)
1661 {
1662     GROUP_SCREEN (w->screen);
1663     GROUP_WINDOW (w);
1664 
1665     UNWRAP (gs, w->screen, getOutputExtentsForWindow);
1666     (*w->screen->getOutputExtentsForWindow) (w, output);
1667     WRAP (gs, w->screen, getOutputExtentsForWindow,
1668 	  groupGetOutputExtentsForWindow);
1669 
1670     if (gw->group && gw->group->nWins > 1)
1671     {
1672 	GROUP_DISPLAY (w->screen->display);
1673 
1674 	int glowSize = groupGetGlowSize (w->screen);
1675 	int glowType = groupGetGlowType (w->screen);
1676 	int glowTextureSize = gd->glowTextureProperties[glowType].textureSize;
1677 	int glowOffset = gd->glowTextureProperties[glowType].glowOffset;
1678 
1679 	glowSize = glowSize * (glowTextureSize - glowOffset) / glowTextureSize;
1680 
1681 	/* glowSize is the size of the glow outside the window decoration
1682 	 * (w->input), while w->output includes the size of w->input
1683 	 * this is why we have to add w->input here */
1684 	output->left   = MAX (output->left, glowSize + w->input.left);
1685 	output->right  = MAX (output->right, glowSize + w->input.right);
1686 	output->top    = MAX (output->top, glowSize + w->input.top);
1687 	output->bottom = MAX (output->bottom, glowSize + w->input.bottom);
1688     }
1689 }
1690 
1691 /*
1692  * groupWindowResizeNotify
1693  *
1694  */
1695 void
groupWindowResizeNotify(CompWindow * w,int dx,int dy,int dwidth,int dheight)1696 groupWindowResizeNotify (CompWindow *w,
1697 			 int        dx,
1698 			 int        dy,
1699 			 int        dwidth,
1700 			 int        dheight)
1701 {
1702     CompScreen *s = w->screen;
1703 
1704     GROUP_SCREEN (s);
1705     GROUP_WINDOW (w);
1706 
1707     if (gw->resizeGeometry)
1708     {
1709 	free (gw->resizeGeometry);
1710 	gw->resizeGeometry = NULL;
1711     }
1712 
1713     UNWRAP (gs, s, windowResizeNotify);
1714     (*s->windowResizeNotify) (w, dx, dy, dwidth, dheight);
1715     WRAP (gs, s, windowResizeNotify, groupWindowResizeNotify);
1716 
1717     if (gw->glowQuads)
1718 	groupComputeGlowQuads (w, &gs->glowTexture.matrix);
1719 
1720     if (gw->group && gw->group->tabBar && IS_TOP_TAB (w, gw->group))
1721     {
1722 	if (gw->group->tabBar->state != PaintOff)
1723 	{
1724 	    groupRecalcTabBarPos (gw->group, pointerX,
1725 				  WIN_X (w), WIN_X (w) + WIN_WIDTH (w));
1726 	}
1727     }
1728 }
1729 
1730 /*
1731  * groupWindowMoveNotify
1732  *
1733  */
1734 void
groupWindowMoveNotify(CompWindow * w,int dx,int dy,Bool immediate)1735 groupWindowMoveNotify (CompWindow *w,
1736 		       int        dx,
1737 		       int        dy,
1738 		       Bool       immediate)
1739 {
1740     CompScreen *s = w->screen;
1741     Bool       viewportChange;
1742     int        i;
1743 
1744     GROUP_SCREEN (s);
1745     GROUP_DISPLAY (s->display);
1746     GROUP_WINDOW (w);
1747 
1748     UNWRAP (gs, s, windowMoveNotify);
1749     (*s->windowMoveNotify) (w, dx, dy, immediate);
1750     WRAP (gs, s, windowMoveNotify, groupWindowMoveNotify);
1751 
1752     if (gw->glowQuads)
1753 	groupComputeGlowQuads (w, &gs->glowTexture.matrix);
1754 
1755     if (!gw->group || gs->queued)
1756 	return;
1757 
1758     /* FIXME: we need a reliable, 100% safe way to detect window
1759        moves caused by viewport changes here */
1760     viewportChange = ((dx && !(dx % w->screen->width)) ||
1761 		      (dy && !(dy % w->screen->height)));
1762 
1763     if (viewportChange && (gw->animateState & IS_ANIMATED))
1764     {
1765 	gw->destination.x += dx;
1766 	gw->destination.y += dy;
1767     }
1768 
1769     if (gw->group->tabBar && IS_TOP_TAB (w, gw->group))
1770     {
1771 	GroupTabBarSlot *slot;
1772 	GroupTabBar     *bar = gw->group->tabBar;
1773 
1774 	bar->rightSpringX += dx;
1775 	bar->leftSpringX += dx;
1776 
1777 	groupMoveTabBarRegion (gw->group, dx, dy, TRUE);
1778 
1779 	for (slot = bar->slots; slot; slot = slot->next)
1780 	{
1781 	    XOffsetRegion (slot->region, dx, dy);
1782 	    slot->springX += dx;
1783 	}
1784     }
1785 
1786     if (!groupGetMoveAll (s) || gd->ignoreMode ||
1787 	(gw->group->tabbingState != NoTabbing) ||
1788 	(gw->group->grabWindow != w->id) ||
1789 	!(gw->group->grabMask & CompWindowGrabMoveMask))
1790     {
1791 	return;
1792     }
1793 
1794     for (i = 0; i < gw->group->nWins; i++)
1795     {
1796 	CompWindow *cw = gw->group->windows[i];
1797 	if (!cw)
1798 	    continue;
1799 
1800 	if (cw->id == w->id)
1801 	    continue;
1802 
1803 	GROUP_WINDOW (cw);
1804 
1805 	if (cw->state & MAXIMIZE_STATE)
1806 	{
1807 	    if (viewportChange)
1808 		groupEnqueueMoveNotify (cw, -dx, -dy, immediate, TRUE);
1809 	}
1810 	else if (!viewportChange)
1811 	{
1812 	    gw->needsPosSync = TRUE;
1813 	    groupEnqueueMoveNotify (cw, dx, dy, immediate, FALSE);
1814 	}
1815     }
1816 }
1817 
1818 void
groupWindowGrabNotify(CompWindow * w,int x,int y,unsigned int state,unsigned int mask)1819 groupWindowGrabNotify (CompWindow   *w,
1820 		       int          x,
1821 		       int          y,
1822 		       unsigned int state,
1823 		       unsigned int mask)
1824 {
1825     CompScreen *s = w->screen;
1826 
1827     GROUP_SCREEN (s);
1828     GROUP_DISPLAY (s->display);
1829     GROUP_WINDOW (w);
1830 
1831     if (gw->group && !gd->ignoreMode && !gs->queued)
1832     {
1833 	Bool doResizeAll;
1834 	int  i;
1835 
1836 	doResizeAll = groupGetResizeAll (s) &&
1837 	              (mask & CompWindowGrabResizeMask);
1838 
1839 	if (gw->group->tabBar)
1840 	    groupTabSetVisibility (gw->group, FALSE, 0);
1841 
1842 	for (i = 0; i < gw->group->nWins; i++)
1843 	{
1844 	    CompWindow *cw = gw->group->windows[i];
1845 	    if (!cw)
1846 		continue;
1847 
1848 	    if (cw->id != w->id)
1849 	    {
1850 		GroupWindow *gcw = GET_GROUP_WINDOW (cw, gs);
1851 
1852 		groupEnqueueGrabNotify (cw, x, y, state, mask);
1853 
1854 		if (doResizeAll && !(cw->state & MAXIMIZE_STATE))
1855 		{
1856 		    if (!gcw->resizeGeometry)
1857 			gcw->resizeGeometry = malloc (sizeof (XRectangle));
1858 		    if (gcw->resizeGeometry)
1859 		    {
1860 			gcw->resizeGeometry->x      = WIN_X (cw);
1861 			gcw->resizeGeometry->y      = WIN_Y (cw);
1862 			gcw->resizeGeometry->width  = WIN_WIDTH (cw);
1863 			gcw->resizeGeometry->height = WIN_HEIGHT (cw);
1864 		    }
1865 		}
1866 	    }
1867 	}
1868 
1869 	if (doResizeAll)
1870 	{
1871 	    if (!gd->resizeInfo)
1872 		gd->resizeInfo = malloc (sizeof (GroupResizeInfo));
1873 
1874 	    if (gd->resizeInfo)
1875 	    {
1876 		gd->resizeInfo->resizedWindow       = w;
1877 		gd->resizeInfo->origGeometry.x      = WIN_X (w);
1878 		gd->resizeInfo->origGeometry.y      = WIN_Y (w);
1879 		gd->resizeInfo->origGeometry.width  = WIN_WIDTH (w);
1880 		gd->resizeInfo->origGeometry.height = WIN_HEIGHT (w);
1881 	    }
1882 	}
1883 
1884 	gw->group->grabWindow = w->id;
1885 	gw->group->grabMask = mask;
1886     }
1887 
1888     UNWRAP (gs, s, windowGrabNotify);
1889     (*s->windowGrabNotify) (w, x, y, state, mask);
1890     WRAP (gs, s, windowGrabNotify, groupWindowGrabNotify);
1891 }
1892 
1893 void
groupWindowUngrabNotify(CompWindow * w)1894 groupWindowUngrabNotify (CompWindow *w)
1895 {
1896     CompScreen *s = w->screen;
1897 
1898     GROUP_SCREEN (s);
1899     GROUP_DISPLAY (s->display);
1900     GROUP_WINDOW (w);
1901 
1902     if (gw->group && !gd->ignoreMode && !gs->queued)
1903     {
1904 	int        i;
1905 	XRectangle rect;
1906 
1907 	groupDequeueMoveNotifies (s);
1908 
1909 	if (gd->resizeInfo)
1910 	{
1911 	    rect.x      = WIN_X (w);
1912 	    rect.y      = WIN_Y (w);
1913 	    rect.width  = WIN_WIDTH (w);
1914 	    rect.height = WIN_HEIGHT (w);
1915 	}
1916 
1917 	for (i = 0; i < gw->group->nWins; i++)
1918 	{
1919 	    CompWindow *cw = gw->group->windows[i];
1920 	    if (!cw)
1921 		continue;
1922 
1923 	    if (cw->id != w->id)
1924 	    {
1925 		GROUP_WINDOW (cw);
1926 
1927 		if (gw->resizeGeometry)
1928 		{
1929 		    unsigned int mask;
1930 
1931 		    gw->resizeGeometry->x      = WIN_X (cw);
1932 		    gw->resizeGeometry->y      = WIN_Y (cw);
1933 		    gw->resizeGeometry->width  = WIN_WIDTH (cw);
1934 		    gw->resizeGeometry->height = WIN_HEIGHT (cw);
1935 
1936 		    mask = groupUpdateResizeRectangle (cw, &rect, FALSE);
1937 		    if (mask)
1938 		    {
1939 			XWindowChanges xwc;
1940 			xwc.x      = gw->resizeGeometry->x;
1941 			xwc.y      = gw->resizeGeometry->y;
1942 			xwc.width  = gw->resizeGeometry->width;
1943 			xwc.height = gw->resizeGeometry->height;
1944 
1945 			if (w->mapNum && (mask & (CWWidth | CWHeight)))
1946 			    sendSyncRequest (w);
1947 
1948 			configureXWindow (cw, mask, &xwc);
1949 		    }
1950 		    else
1951 		    {
1952 			free (gw->resizeGeometry);
1953 			gw->resizeGeometry =  NULL;
1954 		    }
1955 		}
1956 		if (gw->needsPosSync)
1957 		{
1958 		    syncWindowPosition (cw);
1959 		    gw->needsPosSync = FALSE;
1960 		}
1961 		groupEnqueueUngrabNotify (cw);
1962 	    }
1963 	}
1964 
1965 	if (gd->resizeInfo)
1966 	{
1967 	    free (gd->resizeInfo);
1968 	    gd->resizeInfo = NULL;
1969 	}
1970 
1971 	gw->group->grabWindow = None;
1972 	gw->group->grabMask = 0;
1973     }
1974 
1975     UNWRAP (gs, s, windowUngrabNotify);
1976     (*s->windowUngrabNotify) (w);
1977     WRAP( gs, s, windowUngrabNotify, groupWindowUngrabNotify);
1978 }
1979 
1980 Bool
groupDamageWindowRect(CompWindow * w,Bool initial,BoxPtr rect)1981 groupDamageWindowRect (CompWindow *w,
1982 		       Bool       initial,
1983 		       BoxPtr     rect)
1984 {
1985     Bool       status;
1986     CompScreen *s = w->screen;
1987 
1988     GROUP_SCREEN (s);
1989     GROUP_WINDOW (w);
1990 
1991     UNWRAP (gs, s, damageWindowRect);
1992     status = (*s->damageWindowRect) (w,initial,rect);
1993     WRAP (gs, s, damageWindowRect, groupDamageWindowRect);
1994 
1995     if (initial)
1996     {
1997 	if (groupGetAutotabCreate (s) && groupIsGroupWindow (w))
1998 	{
1999 	    if (!gw->group && (gw->windowState == WindowNormal))
2000 	    {
2001 		groupAddWindowToGroup (w, NULL, 0);
2002 		groupTabGroup (w);
2003 	    }
2004 	}
2005 
2006 	if (gw->group)
2007 	{
2008 	    if (gw->windowState == WindowMinimized)
2009 	    {
2010 		if (groupGetMinimizeAll (s))
2011 		    groupMinimizeWindows (w, gw->group, FALSE);
2012 	    }
2013 	    else if (gw->windowState == WindowShaded)
2014 	    {
2015 		if (groupGetShadeAll (s))
2016 		    groupShadeWindows (w, gw->group, FALSE);
2017 	    }
2018 	}
2019 
2020 	gw->windowState = WindowNormal;
2021     }
2022 
2023     if (gw->resizeGeometry)
2024     {
2025 	BoxRec box;
2026 
2027 	groupGetStretchRectangle (w, &box, NULL, NULL);
2028 	groupDamagePaintRectangle (s, &box);
2029     }
2030 
2031     if (gw->slot)
2032     {
2033 	int    vx, vy;
2034 	Region reg;
2035 
2036 	groupGetDrawOffsetForSlot (gw->slot, &vx, &vy);
2037 	if (vx || vy)
2038 	{
2039 	    reg = XCreateRegion ();
2040 	    XUnionRegion (reg, gw->slot->region, reg);
2041 	    XOffsetRegion (reg, vx, vy);
2042 	}
2043 	else
2044 	    reg = gw->slot->region;
2045 
2046 	damageScreenRegion (s, reg);
2047 
2048 	if (vx || vy)
2049 	    XDestroyRegion (reg);
2050     }
2051 
2052     return status;
2053 }
2054 
2055 void
groupWindowStateChangeNotify(CompWindow * w,unsigned int lastState)2056 groupWindowStateChangeNotify (CompWindow   *w,
2057 			      unsigned int lastState)
2058 {
2059     CompScreen *s = w->screen;
2060 
2061     GROUP_DISPLAY (s->display);
2062     GROUP_SCREEN (s);
2063     GROUP_WINDOW (w);
2064 
2065     if (gw->group && !gd->ignoreMode)
2066     {
2067 	if (((lastState & MAXIMIZE_STATE) != (w->state & MAXIMIZE_STATE)) &&
2068 	    groupGetMaximizeUnmaximizeAll (s))
2069 	{
2070 	    int i;
2071 	    for (i = 0; i < gw->group->nWins; i++)
2072 	    {
2073 		CompWindow *cw = gw->group->windows[i];
2074 		if (!cw)
2075 		    continue;
2076 
2077 		if (cw->id == w->id)
2078 		    continue;
2079 
2080 		maximizeWindow (cw, w->state & MAXIMIZE_STATE);
2081 	    }
2082 	}
2083     }
2084 
2085     UNWRAP (gs, s, windowStateChangeNotify);
2086     (*s->windowStateChangeNotify) (w, lastState);
2087     WRAP (gs, s, windowStateChangeNotify, groupWindowStateChangeNotify);
2088 }
2089 
2090 void
groupActivateWindow(CompWindow * w)2091 groupActivateWindow (CompWindow *w)
2092 {
2093     CompScreen *s = w->screen;
2094 
2095     GROUP_SCREEN (s);
2096     GROUP_WINDOW (w);
2097 
2098     if (gw->group && gw->group->tabBar && !IS_TOP_TAB (w, gw->group))
2099 	groupChangeTab (gw->slot, RotateUncertain);
2100 
2101     UNWRAP (gs, s, activateWindow);
2102     (*s->activateWindow) (w);
2103     WRAP (gs, s, activateWindow, groupActivateWindow);
2104 }
2105