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 = ®.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, ®);
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, ®);
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