1 /**
2 *
3 * Compiz group plugin
4 *
5 * tab.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 * groupGetCurrentMousePosition
29 *
30 * Description:
31 * Return the current function of the pointer at the given screen.
32 * The position is queried trough XQueryPointer directly from the xserver.
33 *
34 */
35 Bool
groupGetCurrentMousePosition(CompScreen * s,int * x,int * y)36 groupGetCurrentMousePosition (CompScreen *s,
37 int *x,
38 int *y)
39 {
40 unsigned int rmask;
41 int mouseX, mouseY, winX, winY;
42 Window root;
43 Window child;
44 Bool result;
45
46 result = XQueryPointer (s->display->display, s->root, &root,
47 &child, &mouseX, &mouseY, &winX, &winY, &rmask);
48
49 if (result)
50 {
51 (*x) = mouseX;
52 (*y) = mouseY;
53 }
54
55 return result;
56 }
57
58 /*
59 * groupGetClippingRegion
60 *
61 * Description:
62 * This function returns a clipping region which is used to clip
63 * several events involving window stack such as hover detection
64 * in the tab bar or Drag'n'Drop. It creates the clipping region
65 * with getting the region of every window above the given window
66 * and then adds this region to the clipping region using
67 * XUnionRectWithRegion. w->region won't work since it doesn't include
68 * the window decoration.
69 *
70 */
71 Region
groupGetClippingRegion(CompWindow * w)72 groupGetClippingRegion (CompWindow *w)
73 {
74 CompWindow *cw;
75 Region clip;
76
77 clip = XCreateRegion ();
78 if (!clip)
79 return NULL;
80
81 for (cw = w->next; cw; cw = cw->next)
82 {
83 if (!cw->invisible && !(cw->state & CompWindowStateHiddenMask))
84 {
85 XRectangle rect;
86 Region buf;
87
88 buf = XCreateRegion ();
89 if (!buf)
90 {
91 XDestroyRegion (clip);
92 return NULL;
93 }
94
95 rect.x = WIN_REAL_X (cw);
96 rect.y = WIN_REAL_Y (cw);
97 rect.width = WIN_REAL_WIDTH (cw);
98 rect.height = WIN_REAL_HEIGHT (cw);
99 XUnionRectWithRegion (&rect, buf, buf);
100
101 XUnionRegion (clip, buf, clip);
102 XDestroyRegion (buf);
103 }
104 }
105
106 return clip;
107 }
108
109
110 /*
111 * groupClearWindowInputShape
112 *
113 */
114 void
groupClearWindowInputShape(CompWindow * w,GroupWindowHideInfo * hideInfo)115 groupClearWindowInputShape (CompWindow *w,
116 GroupWindowHideInfo *hideInfo)
117 {
118 XRectangle *rects;
119 int count = 0, ordering;
120 CompDisplay *d = w->screen->display;
121
122 rects = XShapeGetRectangles (d->display, w->id, ShapeInput,
123 &count, &ordering);
124
125 if (count == 0)
126 return;
127
128 /* check if the returned shape exactly matches the window shape -
129 if that is true, the window currently has no set input shape */
130 if ((count == 1) &&
131 (rects[0].x == -w->serverBorderWidth) &&
132 (rects[0].y == -w->serverBorderWidth) &&
133 (rects[0].width == (w->serverWidth + w->serverBorderWidth)) &&
134 (rects[0].height == (w->serverHeight + w->serverBorderWidth)))
135 {
136 count = 0;
137 }
138
139 if (hideInfo->inputRects)
140 XFree (hideInfo->inputRects);
141
142 hideInfo->inputRects = rects;
143 hideInfo->nInputRects = count;
144 hideInfo->inputRectOrdering = ordering;
145
146 XShapeSelectInput (d->display, w->id, NoEventMask);
147
148 XShapeCombineRectangles (d->display, w->id, ShapeInput, 0, 0,
149 NULL, 0, ShapeSet, 0);
150
151 XShapeSelectInput (d->display, w->id, ShapeNotify);
152 }
153
154 /*
155 * groupSetWindowVisibility
156 *
157 */
158 void
groupSetWindowVisibility(CompWindow * w,Bool visible)159 groupSetWindowVisibility (CompWindow *w,
160 Bool visible)
161 {
162 CompDisplay *d = w->screen->display;
163
164 GROUP_WINDOW (w);
165
166 if (!visible && !gw->windowHideInfo)
167 {
168 GroupWindowHideInfo *info;
169
170 gw->windowHideInfo = info = malloc (sizeof (GroupWindowHideInfo));
171 if (!gw->windowHideInfo)
172 return;
173
174 info->inputRects = NULL;
175 info->nInputRects = 0;
176 info->shapeMask = XShapeInputSelected (d->display, w->id);
177 groupClearWindowInputShape (w, info);
178
179 if (w->frame)
180 {
181 info->frameWindow = w->frame;
182 XUnmapWindow (d->display, w->frame);
183 } else
184 info->frameWindow = None;
185
186 info->skipState = w->state & (CompWindowStateSkipPagerMask |
187 CompWindowStateSkipTaskbarMask);
188
189 changeWindowState (w, w->state |
190 CompWindowStateSkipPagerMask |
191 CompWindowStateSkipTaskbarMask);
192 }
193 else if (visible && gw->windowHideInfo)
194 {
195 GroupWindowHideInfo *info = gw->windowHideInfo;
196
197 if (info->nInputRects)
198 {
199 XShapeCombineRectangles (d->display, w->id, ShapeInput, 0, 0,
200 info->inputRects, info->nInputRects,
201 ShapeSet, info->inputRectOrdering);
202 }
203 else
204 {
205 XShapeCombineMask (d->display, w->id, ShapeInput,
206 0, 0, None, ShapeSet);
207 }
208
209 if (info->inputRects)
210 XFree (info->inputRects);
211
212 XShapeSelectInput (d->display, w->id, info->shapeMask);
213
214 if (info->frameWindow)
215 {
216 if (w->attrib.map_state != IsUnmapped)
217 XMapWindow (d->display, w->frame);
218 }
219
220 changeWindowState (w,
221 (w->state & ~(CompWindowStateSkipPagerMask |
222 CompWindowStateSkipTaskbarMask)) |
223 info->skipState);
224
225 free (info);
226 gw->windowHideInfo = NULL;
227 }
228 }
229
230 /*
231 * groupTabBarTimeout
232 *
233 * Description:
234 * This function is called when the time expired (== timeout).
235 * We use this to realize a delay with the bar hiding after tab change.
236 * groupHandleAnimation sets up a timer after the animation has finished.
237 * This function itself basically just sets the tab bar to a PaintOff status
238 * through calling groupSetTabBarVisibility.
239 * The PERMANENT mask allows you to force hiding even of
240 * PaintPermanentOn tab bars.
241 *
242 */
243 static Bool
groupTabBarTimeout(void * data)244 groupTabBarTimeout (void *data)
245 {
246 GroupSelection *group = (GroupSelection *) data;
247
248 groupTabSetVisibility (group, FALSE, PERMANENT);
249
250 group->tabBar->timeoutHandle = 0;
251
252 return FALSE; /* This will free the timer. */
253 }
254
255 /*
256 * groupShowDelayTimeout
257 *
258 */
259 static Bool
groupShowDelayTimeout(void * data)260 groupShowDelayTimeout (void *data)
261 {
262 int mouseX, mouseY;
263 GroupSelection *group = (GroupSelection *) data;
264 CompScreen *s = group->screen;
265 CompWindow *topTab;
266
267 GROUP_SCREEN (s);
268
269 if (!HAS_TOP_WIN (group))
270 {
271 gs->showDelayTimeoutHandle = 0;
272 return FALSE; /* This will free the timer. */
273 }
274
275 topTab = TOP_TAB (group);
276
277 groupGetCurrentMousePosition (s, &mouseX, &mouseY);
278
279 groupRecalcTabBarPos (group, mouseX, WIN_REAL_X (topTab),
280 WIN_REAL_X (topTab) + WIN_REAL_WIDTH (topTab));
281
282 groupTabSetVisibility (group, TRUE, 0);
283
284 gs->showDelayTimeoutHandle = 0;
285 return FALSE; /* This will free the timer. */
286 }
287
288 /*
289 * groupTabSetVisibility
290 *
291 * Description:
292 * This function is used to set the visibility of the tab bar.
293 * The "visibility" is indicated through the PaintState, which
294 * can be PaintOn, PaintOff, PaintFadeIn, PaintFadeOut
295 * and PaintPermantOn.
296 * Currently the mask paramater is mostely used for the PERMANENT mask.
297 * This mask affects how the visible parameter is handled, for example if
298 * visibule is set to TRUE and the mask to PERMANENT state it will set
299 * PaintPermanentOn state for the tab bar. When visibile is FALSE, mask 0
300 * and the current state of the tab bar is PaintPermanentOn it won't do
301 * anything because its not strong enough to disable a
302 * Permanent-State, for those you need the mask.
303 *
304 */
305 void
groupTabSetVisibility(GroupSelection * group,Bool visible,unsigned int mask)306 groupTabSetVisibility (GroupSelection *group,
307 Bool visible,
308 unsigned int mask)
309 {
310 GroupTabBar *bar;
311 CompWindow *topTab;
312 PaintState oldState;
313 CompScreen *s;
314
315 if (!group || !group->windows || !group->tabBar || !HAS_TOP_WIN (group))
316 return;
317
318 s = group->screen;
319 bar = group->tabBar;
320 topTab = TOP_TAB (group);
321 oldState = bar->state;
322
323 /* hide tab bars for invisible top windows */
324 if ((topTab->state & CompWindowStateHiddenMask) || topTab->invisible)
325 {
326 bar->state = PaintOff;
327 groupSwitchTopTabInput (group, TRUE);
328 }
329 else if (visible && bar->state != PaintPermanentOn && (mask & PERMANENT))
330 {
331 bar->state = PaintPermanentOn;
332 groupSwitchTopTabInput (group, FALSE);
333 }
334 else if (visible && bar->state == PaintPermanentOn && !(mask & PERMANENT))
335 {
336 bar->state = PaintOn;
337 }
338 else if (visible && (bar->state == PaintOff || bar->state == PaintFadeOut))
339 {
340 if (groupGetBarAnimations (s))
341 {
342 bar->bgAnimation = AnimationReflex;
343 bar->bgAnimationTime = groupGetReflexTime (s) * 1000.0;
344 }
345 bar->state = PaintFadeIn;
346 groupSwitchTopTabInput (group, FALSE);
347 }
348 else if (!visible &&
349 (bar->state != PaintPermanentOn || (mask & PERMANENT)) &&
350 (bar->state == PaintOn || bar->state == PaintPermanentOn ||
351 bar->state == PaintFadeIn))
352 {
353 bar->state = PaintFadeOut;
354 groupSwitchTopTabInput (group, TRUE);
355 }
356
357 if (bar->state == PaintFadeIn || bar->state == PaintFadeOut)
358 bar->animationTime = (groupGetFadeTime (s) * 1000) - bar->animationTime;
359
360 if (bar->state != oldState)
361 groupDamageTabBarRegion (group);
362 }
363
364 /*
365 * groupGetDrawOffsetForSlot
366 *
367 * Description:
368 * Its used when the draggedSlot is dragged to another viewport.
369 * It calculates a correct offset to the real slot position.
370 *
371 */
372 void
groupGetDrawOffsetForSlot(GroupTabBarSlot * slot,int * hoffset,int * voffset)373 groupGetDrawOffsetForSlot (GroupTabBarSlot *slot,
374 int *hoffset,
375 int *voffset)
376 {
377 CompWindow *w, *topTab;
378 CompScreen *s;
379 int vx, vy, x, y;
380
381 if (!slot || !slot->window)
382 return;
383
384 w = slot->window;
385 s = w->screen;
386
387 GROUP_WINDOW (w);
388 GROUP_SCREEN (s);
389
390 if (slot != gs->draggedSlot)
391 {
392 if (hoffset)
393 *hoffset = 0;
394 if (voffset)
395 *voffset = 0;
396
397 return;
398 }
399
400 if (HAS_TOP_WIN (gw->group))
401 topTab = TOP_TAB (gw->group);
402 else if (HAS_PREV_TOP_WIN (gw->group))
403 topTab = PREV_TOP_TAB (gw->group);
404 else
405 {
406 if (hoffset)
407 *hoffset = 0;
408 if (voffset)
409 *voffset = 0;
410 return;
411 }
412
413 x = WIN_CENTER_X (topTab) - WIN_WIDTH (w) / 2;
414 y = WIN_CENTER_Y (topTab) - WIN_HEIGHT (w) / 2;
415
416 viewportForGeometry (s, x, y, w->serverWidth, w->serverHeight,
417 w->serverBorderWidth, &vx, &vy);
418
419 if (hoffset)
420 *hoffset = ((s->x - vx) % s->hsize) * s->width;
421
422 if (voffset)
423 *voffset = ((s->y - vy) % s->vsize) * s->height;
424 }
425
426 /*
427 * groupHandleHoverDetection
428 *
429 * Description:
430 * This function is called from groupPreparePaintScreen to handle
431 * the hover detection. This is needed for the text showing up,
432 * when you hover a thumb on the thumb bar.
433 *
434 * FIXME: we should better have a timer for that ...
435 */
436 void
groupHandleHoverDetection(GroupSelection * group)437 groupHandleHoverDetection (GroupSelection *group)
438 {
439 GroupTabBar *bar = group->tabBar;
440 CompWindow *topTab = TOP_TAB (group);
441 int mouseX, mouseY;
442 Bool mouseOnScreen, inLastSlot;
443
444 /* first get the current mouse position */
445 mouseOnScreen = groupGetCurrentMousePosition (group->screen,
446 &mouseX, &mouseY);
447
448 if (!mouseOnScreen)
449 return;
450
451 /* then check if the mouse is in the last hovered slot --
452 this saves a lot of CPU usage */
453 inLastSlot = bar->hoveredSlot &&
454 XPointInRegion (bar->hoveredSlot->region, mouseX, mouseY);
455
456 if (!inLastSlot)
457 {
458 Region clip;
459 GroupTabBarSlot *slot;
460
461 bar->hoveredSlot = NULL;
462 clip = groupGetClippingRegion (topTab);
463
464 for (slot = bar->slots; slot; slot = slot->next)
465 {
466 /* We need to clip the slot region with the clip region first.
467 This is needed to respect the window stack, so if a window
468 covers a port of that slot, this part won't be used
469 for in-slot-detection. */
470 Region reg = XCreateRegion();
471 if (!reg)
472 {
473 XDestroyRegion(clip);
474 return;
475 }
476
477 XSubtractRegion (slot->region, clip, reg);
478
479 if (XPointInRegion (reg, mouseX, mouseY))
480 {
481 bar->hoveredSlot = slot;
482 XDestroyRegion (reg);
483 break;
484 }
485
486 XDestroyRegion (reg);
487 }
488
489 XDestroyRegion (clip);
490
491 if (bar->textLayer)
492 {
493 /* trigger a FadeOut of the text */
494 if ((bar->hoveredSlot != bar->textSlot) &&
495 (bar->textLayer->state == PaintFadeIn ||
496 bar->textLayer->state == PaintOn))
497 {
498 bar->textLayer->animationTime =
499 (groupGetFadeTextTime (group->screen) * 1000) -
500 bar->textLayer->animationTime;
501 bar->textLayer->state = PaintFadeOut;
502 }
503
504 /* or trigger a FadeIn of the text */
505 else if (bar->textLayer->state == PaintFadeOut &&
506 bar->hoveredSlot == bar->textSlot && bar->hoveredSlot)
507 {
508 bar->textLayer->animationTime =
509 (groupGetFadeTextTime (group->screen) * 1000) -
510 bar->textLayer->animationTime;
511 bar->textLayer->state = PaintFadeIn;
512 }
513 }
514 }
515 }
516
517 /*
518 * groupHandleTabBarFade
519 *
520 * Description:
521 * This function is called from groupPreparePaintScreen
522 * to handle the tab bar fade. It checks the animationTime and updates it,
523 * so we can calculate the alpha of the tab bar in the painting code with it.
524 *
525 */
526 void
groupHandleTabBarFade(GroupSelection * group,int msSinceLastPaint)527 groupHandleTabBarFade (GroupSelection *group,
528 int msSinceLastPaint)
529 {
530 GroupTabBar *bar = group->tabBar;
531
532 bar->animationTime -= msSinceLastPaint;
533
534 if (bar->animationTime < 0)
535 bar->animationTime = 0;
536
537 /* Fade finished */
538 if (bar->animationTime == 0)
539 {
540 if (bar->state == PaintFadeIn)
541 {
542 bar->state = PaintOn;
543 }
544 else if (bar->state == PaintFadeOut)
545 {
546 bar->state = PaintOff;
547
548 if (bar->textLayer)
549 {
550 /* Tab-bar is no longer painted, clean up
551 text animation variables. */
552 bar->textLayer->animationTime = 0;
553 bar->textLayer->state = PaintOff;
554 bar->textSlot = bar->hoveredSlot = NULL;
555
556 groupRenderWindowTitle (group);
557 }
558 }
559 }
560 }
561
562 /*
563 * groupHandleTextFade
564 *
565 * Description:
566 * This function is called from groupPreparePaintScreen
567 * to handle the text fade. It checks the animationTime and updates it,
568 * so we can calculate the alpha of the text in the painting code with it.
569 *
570 */
571 void
groupHandleTextFade(GroupSelection * group,int msSinceLastPaint)572 groupHandleTextFade (GroupSelection *group,
573 int msSinceLastPaint)
574 {
575 GroupTabBar *bar = group->tabBar;
576 GroupCairoLayer *textLayer = bar->textLayer;
577
578 /* Fade in progress... */
579 if ((textLayer->state == PaintFadeIn || textLayer->state == PaintFadeOut) &&
580 textLayer->animationTime > 0)
581 {
582 textLayer->animationTime -= msSinceLastPaint;
583
584 if (textLayer->animationTime < 0)
585 textLayer->animationTime = 0;
586
587 /* Fade has finished. */
588 if (textLayer->animationTime == 0)
589 {
590 if (textLayer->state == PaintFadeIn)
591 textLayer->state = PaintOn;
592
593 else if (textLayer->state == PaintFadeOut)
594 textLayer->state = PaintOff;
595 }
596 }
597
598 if (textLayer->state == PaintOff && bar->hoveredSlot)
599 {
600 /* Start text animation for the new hovered slot. */
601 bar->textSlot = bar->hoveredSlot;
602 textLayer->state = PaintFadeIn;
603 textLayer->animationTime =
604 (groupGetFadeTextTime (group->screen) * 1000);
605
606 groupRenderWindowTitle (group);
607 }
608
609 else if (textLayer->state == PaintOff && bar->textSlot)
610 {
611 /* Clean Up. */
612 bar->textSlot = NULL;
613 groupRenderWindowTitle (group);
614 }
615 }
616
617 /*
618 * groupHandleTabBarAnimation
619 *
620 * Description: Handles the different animations for the tab bar defined in
621 * GroupAnimationType. Basically that means this function updates
622 * tabBar->animation->time as well as checking if the animation is already
623 * finished.
624 *
625 */
626 void
groupHandleTabBarAnimation(GroupSelection * group,int msSinceLastPaint)627 groupHandleTabBarAnimation (GroupSelection *group,
628 int msSinceLastPaint)
629 {
630 GroupTabBar *bar = group->tabBar;
631
632 bar->bgAnimationTime -= msSinceLastPaint;
633
634 if (bar->bgAnimationTime <= 0)
635 {
636 bar->bgAnimationTime = 0;
637 bar->bgAnimation = 0;
638
639 groupRenderTabBarBackground (group);
640 }
641 }
642
643 /*
644 * groupTabChangeActivateEvent
645 *
646 * Description: Creates a compiz event to let other plugins know about
647 * the starting and ending point of the tab changing animation
648 */
649 static void
groupTabChangeActivateEvent(CompScreen * s,Bool activating)650 groupTabChangeActivateEvent (CompScreen *s,
651 Bool activating)
652 {
653 CompOption o[2];
654
655 o[0].type = CompOptionTypeInt;
656 o[0].name = "root";
657 o[0].value.i = s->root;
658
659 o[1].type = CompOptionTypeBool;
660 o[1].name = "active";
661 o[1].value.b = activating;
662
663 (*s->display->handleCompizEvent) (s->display,
664 "group", "tabChangeActivate", o, 2);
665 }
666
667 /*
668 * groupHandleAnimation
669 *
670 * Description:
671 * This function handles the change animation. It's called
672 * from groupHandleChanges. Don't let the changeState
673 * confuse you, PaintFadeIn equals with the start of the
674 * rotate animation and PaintFadeOut is the end of these
675 * animation.
676 *
677 */
678 void
groupHandleAnimation(GroupSelection * group)679 groupHandleAnimation (GroupSelection *group)
680 {
681 CompScreen *s = group->screen;
682
683 if (group->changeState == TabChangeOldOut)
684 {
685 CompWindow *top = TOP_TAB (group);
686 Bool activate;
687
688 /* recalc here is needed (for y value)! */
689 groupRecalcTabBarPos (group,
690 (group->tabBar->region->extents.x1 +
691 group->tabBar->region->extents.x2) / 2,
692 WIN_REAL_X (top),
693 WIN_REAL_X (top) + WIN_REAL_WIDTH (top));
694
695 group->changeAnimationTime += groupGetChangeAnimationTime (s) * 500;
696
697 if (group->changeAnimationTime <= 0)
698 group->changeAnimationTime = 0;
699
700 group->changeState = TabChangeNewIn;
701
702 activate = !group->checkFocusAfterTabChange;
703 if (!activate)
704 {
705 CompFocusResult focus;
706 focus = allowWindowFocus (top, NO_FOCUS_MASK, s->x, s->y, 0);
707 activate = focus == CompFocusAllowed;
708 }
709
710 if (activate)
711 (*s->activateWindow) (top);
712
713 group->checkFocusAfterTabChange = FALSE;
714 }
715
716 if (group->changeState == TabChangeNewIn &&
717 group->changeAnimationTime <= 0)
718 {
719 int oldChangeAnimationTime = group->changeAnimationTime;
720
721 groupTabChangeActivateEvent (s, FALSE);
722
723 if (group->prevTopTab)
724 groupSetWindowVisibility (PREV_TOP_TAB (group), FALSE);
725
726 group->prevTopTab = group->topTab;
727 group->changeState = NoTabChange;
728
729 if (group->nextTopTab)
730 {
731 GroupTabBarSlot *next = group->nextTopTab;
732 group->nextTopTab = NULL;
733
734 groupChangeTab (next, group->nextDirection);
735
736 if (group->changeState == TabChangeOldOut)
737 {
738 /* If a new animation was started. */
739 group->changeAnimationTime += oldChangeAnimationTime;
740 }
741 }
742
743 if (group->changeAnimationTime <= 0)
744 {
745 group->changeAnimationTime = 0;
746 }
747 else if (groupGetVisibilityTime (s) != 0.0f &&
748 group->changeState == NoTabChange)
749 {
750 groupTabSetVisibility (group, TRUE,
751 PERMANENT | SHOW_BAR_INSTANTLY_MASK);
752
753 if (group->tabBar->timeoutHandle)
754 compRemoveTimeout (group->tabBar->timeoutHandle);
755
756 group->tabBar->timeoutHandle =
757 compAddTimeout (groupGetVisibilityTime (s) * 1000,
758 groupGetVisibilityTime (s) * 1200,
759 groupTabBarTimeout, group);
760 }
761 }
762 }
763
764 /* adjust velocity for each animation step (adapted from the scale plugin) */
765 static int
adjustTabVelocity(CompWindow * w)766 adjustTabVelocity (CompWindow *w)
767 {
768 float dx, dy, adjust, amount;
769 float x1, y1;
770
771 GROUP_WINDOW (w);
772
773 x1 = gw->destination.x;
774 y1 = gw->destination.y;
775
776 dx = x1 - (gw->orgPos.x + gw->tx);
777 adjust = dx * 0.15f;
778 amount = fabs (dx) * 1.5f;
779 if (amount < 0.5f)
780 amount = 0.5f;
781 else if (amount > 5.0f)
782 amount = 5.0f;
783
784 gw->xVelocity = (amount * gw->xVelocity + adjust) / (amount + 1.0f);
785
786 dy = y1 - (gw->orgPos.y + gw->ty);
787 adjust = dy * 0.15f;
788 amount = fabs (dy) * 1.5f;
789 if (amount < 0.5f)
790 amount = 0.5f;
791 else if (amount > 5.0f)
792 amount = 5.0f;
793
794 gw->yVelocity = (amount * gw->yVelocity + adjust) / (amount + 1.0f);
795
796 if (fabs (dx) < 0.1f && fabs (gw->xVelocity) < 0.2f &&
797 fabs (dy) < 0.1f && fabs (gw->yVelocity) < 0.2f)
798 {
799 gw->xVelocity = gw->yVelocity = 0.0f;
800 gw->tx = x1 - w->serverX;
801 gw->ty = y1 - w->serverY;
802
803 return 0;
804 }
805 return 1;
806 }
807
808 static void
groupFinishTabbing(GroupSelection * group)809 groupFinishTabbing (GroupSelection *group)
810 {
811 CompScreen *s = group->screen;
812 int i;
813
814 GROUP_SCREEN (s);
815
816 group->tabbingState = NoTabbing;
817 groupTabChangeActivateEvent (s, FALSE);
818
819 if (group->tabBar)
820 {
821 /* tabbing case - hide all non-toptab windows */
822 GroupTabBarSlot *slot;
823
824 for (slot = group->tabBar->slots; slot; slot = slot->next)
825 {
826 CompWindow *w = slot->window;
827 if (!w)
828 continue;
829
830 GROUP_WINDOW (w);
831
832 if (slot == group->topTab || (gw->animateState & IS_UNGROUPING))
833 continue;
834
835 groupSetWindowVisibility (w, FALSE);
836 }
837 group->prevTopTab = group->topTab;
838 }
839
840 for (i = 0; i < group->nWins; i++)
841 {
842 CompWindow *w = group->windows[i];
843 GROUP_WINDOW (w);
844
845 /* move window to target position */
846 gs->queued = TRUE;
847 moveWindow (w, gw->destination.x - WIN_X (w),
848 gw->destination.y - WIN_Y (w), TRUE, TRUE);
849 gs->queued = FALSE;
850 syncWindowPosition (w);
851
852 if (group->ungroupState == UngroupSingle &&
853 (gw->animateState & IS_UNGROUPING))
854 {
855 groupRemoveWindowFromGroup (w);
856 }
857
858 gw->animateState = 0;
859 gw->tx = gw->ty = gw->xVelocity = gw->yVelocity = 0.0f;
860 }
861
862 if (group->ungroupState == UngroupAll)
863 groupDeleteGroup (group);
864 else
865 group->ungroupState = UngroupNone;
866 }
867
868 /*
869 * groupDrawTabAnimation
870 *
871 * Description:
872 * This function is called from groupPreparePaintScreen, to move
873 * all the animated windows, with the required animation step.
874 * The function goes through all grouped animated windows, calculates
875 * the required step using adjustTabVelocity, moves the window,
876 * and then checks if the animation is finished for that window.
877 *
878 */
879 void
groupDrawTabAnimation(GroupSelection * group,int msSinceLastPaint)880 groupDrawTabAnimation (GroupSelection *group,
881 int msSinceLastPaint)
882 {
883 int steps, i;
884 float amount, chunk;
885 Bool doTabbing;
886 CompScreen *s = group->screen;
887
888 amount = msSinceLastPaint * 0.05f * groupGetTabbingSpeed (s);
889 steps = amount / (0.5f * groupGetTabbingTimestep (s));
890 if (!steps)
891 steps = 1;
892 chunk = amount / (float)steps;
893
894 while (steps--)
895 {
896 doTabbing = FALSE;
897
898 for (i = 0; i < group->nWins; i++)
899 {
900 CompWindow *cw = group->windows[i];
901 if (!cw)
902 continue;
903
904 GROUP_WINDOW (cw);
905
906 if (!(gw->animateState & IS_ANIMATED))
907 continue;
908
909 if (!adjustTabVelocity (cw))
910 {
911 gw->animateState |= FINISHED_ANIMATION;
912 gw->animateState &= ~IS_ANIMATED;
913 }
914
915 gw->tx += gw->xVelocity * chunk;
916 gw->ty += gw->yVelocity * chunk;
917
918 doTabbing |= (gw->animateState & IS_ANIMATED);
919 }
920
921 if (!doTabbing)
922 {
923 /* tabbing animation finished */
924 groupFinishTabbing (group);
925 break;
926 }
927 }
928 }
929
930 /*
931 * groupUpdateTabBars
932 *
933 * Description:
934 * This function is responsible for showing / unshowing the tab-bars,
935 * when the title-bars / tab-bars are hovered.
936 * The function is called whenever a new window is entered,
937 * checks if the entered window is a window frame (and if the title
938 * bar part of that frame was hovered) or if it was the input
939 * prevention window of a tab bar, and sets tab-bar visibility
940 * according to that.
941 *
942 */
943 void
groupUpdateTabBars(CompScreen * s,Window enteredWin)944 groupUpdateTabBars (CompScreen *s,
945 Window enteredWin)
946 {
947 CompWindow *w = NULL;
948 GroupSelection *hoveredGroup = NULL;
949
950 GROUP_SCREEN (s);
951
952 /* do nothing if the screen is grabbed, as the frame might be drawn
953 transformed */
954 if (!otherScreenGrabExist (s, "group", "group-drag", NULL))
955 {
956 /* first check if the entered window is a frame */
957 for (w = s->windows; w; w = w->next)
958 {
959 if (w->frame == enteredWin)
960 break;
961 }
962 }
963
964 if (w)
965 {
966 /* is the window the entered frame belongs to inside
967 a tabbed group? if no, it's not interesting for us */
968 GROUP_WINDOW (w);
969
970 if (gw->group && gw->group->tabBar)
971 {
972 int mouseX, mouseY;
973 /* it is grouped and tabbed, so now we have to
974 check if we hovered the title bar or the frame */
975 if (groupGetCurrentMousePosition (s, &mouseX, &mouseY))
976 {
977 XRectangle rect;
978 Region reg = XCreateRegion();
979 if (!reg)
980 return;
981
982 rect.x = WIN_X (w) - w->input.left;
983 rect.y = WIN_Y (w) - w->input.top;
984 rect.width = WIN_WIDTH (w) + w->input.right;
985 rect.height = WIN_Y (w) - rect.y;
986 XUnionRectWithRegion (&rect, reg, reg);
987
988 if (XPointInRegion (reg, mouseX, mouseY))
989 hoveredGroup = gw->group;
990
991 XDestroyRegion (reg);
992 }
993 }
994 }
995
996 /* if we didn't hover a title bar, check if we hovered
997 a tab bar (means: input prevention window) */
998 if (!hoveredGroup)
999 {
1000 GroupSelection *group;
1001
1002 for (group = gs->groups; group; group = group->next)
1003 {
1004 if (group->inputPrevention == enteredWin)
1005 {
1006 /* only accept it if the IPW is mapped */
1007 if (group->ipwMapped)
1008 {
1009 hoveredGroup = group;
1010 break;
1011 }
1012 }
1013 }
1014 }
1015
1016 /* if we found a hovered tab bar different than the last one
1017 (or left a tab bar), hide the old one */
1018 if (gs->lastHoveredGroup && (hoveredGroup != gs->lastHoveredGroup))
1019 groupTabSetVisibility (gs->lastHoveredGroup, FALSE, 0);
1020
1021 /* if we entered a tab bar (or title bar), show the tab bar */
1022 if (hoveredGroup && HAS_TOP_WIN (hoveredGroup) &&
1023 !TOP_TAB (hoveredGroup)->grabbed)
1024 {
1025 GroupTabBar *bar = hoveredGroup->tabBar;
1026
1027 if (bar && ((bar->state == PaintOff) || (bar->state == PaintFadeOut)))
1028 {
1029 int showDelayTime = groupGetTabbarShowDelay (s) * 1000;
1030
1031 /* Show the tab-bar after a delay,
1032 only if the tab-bar wasn't fading out. */
1033 if (showDelayTime > 0 && (bar->state == PaintOff))
1034 {
1035 if (gs->showDelayTimeoutHandle)
1036 compRemoveTimeout (gs->showDelayTimeoutHandle);
1037 gs->showDelayTimeoutHandle =
1038 compAddTimeout (showDelayTime, (float) showDelayTime * 1.2,
1039 groupShowDelayTimeout, hoveredGroup);
1040 }
1041 else
1042 groupShowDelayTimeout (hoveredGroup);
1043 }
1044 }
1045
1046 gs->lastHoveredGroup = hoveredGroup;
1047 }
1048
1049 /*
1050 * groupGetConstrainRegion
1051 *
1052 */
1053 static Region
groupGetConstrainRegion(CompScreen * s)1054 groupGetConstrainRegion (CompScreen *s)
1055 {
1056 CompWindow *w;
1057 Region region;
1058 REGION r;
1059 int i;
1060
1061 region = XCreateRegion ();
1062 if (!region)
1063 return NULL;
1064
1065 for (i = 0;i < s->nOutputDev; i++)
1066 XUnionRegion (&s->outputDev[i].region, region, region);
1067
1068 r.rects = &r.extents;
1069 r.numRects = r.size = 1;
1070
1071 for (w = s->windows; w; w = w->next)
1072 {
1073 if (!w->mapNum)
1074 continue;
1075
1076 if (w->struts)
1077 {
1078 r.extents.x1 = w->struts->top.x;
1079 r.extents.y1 = w->struts->top.y;
1080 r.extents.x2 = r.extents.x1 + w->struts->top.width;
1081 r.extents.y2 = r.extents.y1 + w->struts->top.height;
1082
1083 XSubtractRegion (region, &r, region);
1084
1085 r.extents.x1 = w->struts->bottom.x;
1086 r.extents.y1 = w->struts->bottom.y;
1087 r.extents.x2 = r.extents.x1 + w->struts->bottom.width;
1088 r.extents.y2 = r.extents.y1 + w->struts->bottom.height;
1089
1090 XSubtractRegion (region, &r, region);
1091
1092 r.extents.x1 = w->struts->left.x;
1093 r.extents.y1 = w->struts->left.y;
1094 r.extents.x2 = r.extents.x1 + w->struts->left.width;
1095 r.extents.y2 = r.extents.y1 + w->struts->left.height;
1096
1097 XSubtractRegion (region, &r, region);
1098
1099 r.extents.x1 = w->struts->right.x;
1100 r.extents.y1 = w->struts->right.y;
1101 r.extents.x2 = r.extents.x1 + w->struts->right.width;
1102 r.extents.y2 = r.extents.y1 + w->struts->right.height;
1103
1104 XSubtractRegion (region, &r, region);
1105 }
1106 }
1107
1108 return region;
1109 }
1110
1111 /*
1112 * groupConstrainMovement
1113 *
1114 */
1115 static Bool
groupConstrainMovement(CompWindow * w,Region constrainRegion,int dx,int dy,int * new_dx,int * new_dy)1116 groupConstrainMovement (CompWindow *w,
1117 Region constrainRegion,
1118 int dx,
1119 int dy,
1120 int *new_dx,
1121 int *new_dy)
1122 {
1123 int status, xStatus;
1124 int origDx = dx, origDy = dy;
1125 int x, y, width, height;
1126
1127 GROUP_WINDOW (w);
1128
1129 if (!gw->group)
1130 return FALSE;
1131
1132 if (!dx && !dy)
1133 return FALSE;
1134
1135 x = gw->orgPos.x - w->input.left + dx;
1136 y = gw->orgPos.y - w->input.top + dy;
1137 width = WIN_REAL_WIDTH (w);
1138 height = WIN_REAL_HEIGHT (w);
1139
1140 status = XRectInRegion (constrainRegion, x, y, width, height);
1141
1142 xStatus = status;
1143 while (dx && (xStatus != RectangleIn))
1144 {
1145 xStatus = XRectInRegion (constrainRegion, x, y - dy, width, height);
1146
1147 if (xStatus != RectangleIn)
1148 dx += (dx < 0) ? 1 : -1;
1149
1150 x = gw->orgPos.x - w->input.left + dx;
1151 }
1152
1153 while (dy && (status != RectangleIn))
1154 {
1155 status = XRectInRegion(constrainRegion, x, y, width, height);
1156
1157 if (status != RectangleIn)
1158 dy += (dy < 0) ? 1 : -1;
1159
1160 y = gw->orgPos.y - w->input.top + dy;
1161 }
1162
1163 if (new_dx)
1164 *new_dx = dx;
1165
1166 if (new_dy)
1167 *new_dy = dy;
1168
1169 return ((dx != origDx) || (dy != origDy));
1170 }
1171
1172 /*
1173 * groupApplyConstraining
1174 *
1175 */
1176 static void
groupApplyConstraining(GroupSelection * group,Region constrainRegion,Window constrainedWindow,int dx,int dy)1177 groupApplyConstraining (GroupSelection *group,
1178 Region constrainRegion,
1179 Window constrainedWindow,
1180 int dx,
1181 int dy)
1182 {
1183 int i;
1184 CompWindow *w;
1185
1186 if (!dx && !dy)
1187 return;
1188
1189 for (i = 0; i < group->nWins; i++)
1190 {
1191 w = group->windows[i];
1192 GROUP_WINDOW (w);
1193
1194 /* ignore certain windows: we don't want to apply the constraining
1195 results on the constrained window itself, nor do we want to
1196 change the target position of unamimated windows and of
1197 windows which already are constrained */
1198 if (w->id == constrainedWindow)
1199 continue;
1200
1201 if (!(gw->animateState & IS_ANIMATED))
1202 continue;
1203
1204 if (gw->animateState & DONT_CONSTRAIN)
1205 continue;
1206
1207 if (!(gw->animateState & CONSTRAINED_X))
1208 {
1209 gw->animateState |= IS_ANIMATED;
1210
1211 /* applying the constraining result of another window
1212 might move the window offscreen, too, so check
1213 if this is not the case */
1214 if (groupConstrainMovement (w, constrainRegion, dx, 0, &dx, NULL))
1215 gw->animateState |= CONSTRAINED_X;
1216
1217 gw->destination.x += dx;
1218 }
1219
1220 if (!(gw->animateState & CONSTRAINED_Y))
1221 {
1222 gw->animateState |= IS_ANIMATED;
1223
1224 /* analog to X case */
1225 if (groupConstrainMovement (w, constrainRegion, 0, dy, NULL, &dy))
1226 gw->animateState |= CONSTRAINED_Y;
1227
1228 gw->destination.y += dy;
1229 }
1230 }
1231 }
1232
1233 /*
1234 * groupStartTabbingAnimation
1235 *
1236 */
1237 void
groupStartTabbingAnimation(GroupSelection * group,Bool tab)1238 groupStartTabbingAnimation (GroupSelection *group,
1239 Bool tab)
1240 {
1241 CompScreen *s;
1242 int i;
1243 int dx, dy;
1244 int constrainStatus;
1245
1246 if (!group || (group->tabbingState != NoTabbing))
1247 return;
1248
1249 s = group->screen;
1250 group->tabbingState = (tab) ? Tabbing : Untabbing;
1251 groupTabChangeActivateEvent (s, TRUE);
1252
1253 if (!tab)
1254 {
1255 /* we need to set up the X/Y constraining on untabbing */
1256 Region constrainRegion = groupGetConstrainRegion (s);
1257 Bool constrainedWindows = TRUE;
1258
1259 if (!constrainRegion)
1260 return;
1261
1262 /* reset all flags */
1263 for (i = 0; i < group->nWins; i++)
1264 {
1265 GROUP_WINDOW (group->windows[i]);
1266 gw->animateState &= ~(CONSTRAINED_X | CONSTRAINED_Y |
1267 DONT_CONSTRAIN);
1268 }
1269
1270 /* as we apply the constraining in a flat loop,
1271 we may need to run multiple times through this
1272 loop until all constraining dependencies are met */
1273 while (constrainedWindows)
1274 {
1275 constrainedWindows = FALSE;
1276 /* loop through all windows and try to constrain their
1277 animation path (going from gw->orgPos to
1278 gw->destination) to the active screen area */
1279 for (i = 0; i < group->nWins; i++)
1280 {
1281 CompWindow *w = group->windows[i];
1282 GROUP_WINDOW (w);
1283
1284 /* ignore windows which aren't animated and/or
1285 already are at the edge of the screen area */
1286 if (!(gw->animateState & IS_ANIMATED))
1287 continue;
1288
1289 if (gw->animateState & DONT_CONSTRAIN)
1290 continue;
1291
1292 /* is the original position inside the screen area? */
1293 constrainStatus = XRectInRegion (constrainRegion,
1294 gw->orgPos.x - w->input.left,
1295 gw->orgPos.y - w->input.top,
1296 WIN_REAL_WIDTH (w),
1297 WIN_REAL_HEIGHT (w));
1298
1299 /* constrain the movement */
1300 if (groupConstrainMovement (w, constrainRegion,
1301 gw->destination.x - gw->orgPos.x,
1302 gw->destination.y - gw->orgPos.y,
1303 &dx, &dy))
1304 {
1305 /* handle the case where the window is outside the screen
1306 area on its whole animation path */
1307 if (constrainStatus != RectangleIn && !dx && !dy)
1308 {
1309 gw->animateState |= DONT_CONSTRAIN;
1310 gw->animateState |= CONSTRAINED_X | CONSTRAINED_Y;
1311
1312 /* use the original position as last resort */
1313 gw->destination.x = gw->mainTabOffset.x;
1314 gw->destination.y = gw->mainTabOffset.y;
1315 }
1316 else
1317 {
1318 /* if we found a valid target position, apply
1319 the change also to other windows to retain
1320 the distance between the windows */
1321 groupApplyConstraining (group, constrainRegion, w->id,
1322 dx - gw->destination.x +
1323 gw->orgPos.x,
1324 dy - gw->destination.y +
1325 gw->orgPos.y);
1326
1327 /* if we hit constraints, adjust the mask and the
1328 target position accordingly */
1329 if (dx != (gw->destination.x - gw->orgPos.x))
1330 {
1331 gw->animateState |= CONSTRAINED_X;
1332 gw->destination.x = gw->orgPos.x + dx;
1333 }
1334
1335 if (dy != (gw->destination.y - gw->orgPos.y))
1336 {
1337 gw->animateState |= CONSTRAINED_Y;
1338 gw->destination.y = gw->orgPos.y + dy;
1339 }
1340
1341 constrainedWindows = TRUE;
1342 }
1343 }
1344 }
1345 }
1346 XDestroyRegion (constrainRegion);
1347 }
1348 }
1349
1350 /*
1351 * groupTabGroup
1352 *
1353 */
1354 void
groupTabGroup(CompWindow * main)1355 groupTabGroup (CompWindow *main)
1356 {
1357 GroupSelection *group;
1358 GroupTabBarSlot *slot;
1359 CompScreen *s = main->screen;
1360 int width, height;
1361 int space, thumbSize;
1362
1363 GROUP_WINDOW (main);
1364
1365 group = gw->group;
1366 if (!group || group->tabBar)
1367 return;
1368
1369 if (!s->display->shapeExtension)
1370 {
1371 compLogMessage ("group", CompLogLevelError,
1372 "No X shape extension! Tabbing disabled.");
1373 return;
1374 }
1375
1376 groupInitTabBar (group, main);
1377 if (!group->tabBar)
1378 return;
1379
1380 groupCreateInputPreventionWindow (group);
1381
1382 group->tabbingState = NoTabbing;
1383 /* Slot is initialized after groupInitTabBar(group); */
1384 groupChangeTab (gw->slot, RotateUncertain);
1385 groupRecalcTabBarPos (gw->group, WIN_CENTER_X (main),
1386 WIN_X (main), WIN_X (main) + WIN_WIDTH (main));
1387
1388 width = group->tabBar->region->extents.x2 -
1389 group->tabBar->region->extents.x1;
1390 height = group->tabBar->region->extents.y2 -
1391 group->tabBar->region->extents.y1;
1392
1393 group->tabBar->textLayer = groupCreateCairoLayer (s, width, height);
1394 if (group->tabBar->textLayer)
1395 {
1396 GroupCairoLayer *layer;
1397
1398 layer = group->tabBar->textLayer;
1399 layer->state = PaintOff;
1400 layer->animationTime = 0;
1401 groupRenderWindowTitle (group);
1402 }
1403 if (group->tabBar->textLayer)
1404 {
1405 GroupCairoLayer *layer;
1406
1407 layer = group->tabBar->textLayer;
1408 layer->animationTime = groupGetFadeTextTime (s) * 1000;
1409 layer->state = PaintFadeIn;
1410 }
1411
1412 /* we need a buffer for DnD here */
1413 space = groupGetThumbSpace (s);
1414 thumbSize = groupGetThumbSize (s);
1415 group->tabBar->bgLayer = groupCreateCairoLayer (s,
1416 width + space + thumbSize,
1417 height);
1418 if (group->tabBar->bgLayer)
1419 {
1420 group->tabBar->bgLayer->state = PaintOn;
1421 group->tabBar->bgLayer->animationTime = 0;
1422 groupRenderTabBarBackground (group);
1423 }
1424
1425 width = group->topTab->region->extents.x2 -
1426 group->topTab->region->extents.x1;
1427 height = group->topTab->region->extents.y2 -
1428 group->topTab->region->extents.y1;
1429
1430 group->tabBar->selectionLayer = groupCreateCairoLayer (s, width, height);
1431 if (group->tabBar->selectionLayer)
1432 {
1433 group->tabBar->selectionLayer->state = PaintOn;
1434 group->tabBar->selectionLayer->animationTime = 0;
1435 groupRenderTopTabHighlight (group);
1436 }
1437
1438 if (!HAS_TOP_WIN (group))
1439 return;
1440
1441 for (slot = group->tabBar->slots; slot; slot = slot->next)
1442 {
1443 CompWindow *cw = slot->window;
1444
1445 GROUP_WINDOW (cw);
1446
1447 if (gw->animateState & (IS_ANIMATED | FINISHED_ANIMATION))
1448 moveWindow (cw,
1449 gw->destination.x - WIN_X (cw),
1450 gw->destination.y - WIN_Y (cw),
1451 FALSE, TRUE);
1452
1453 /* center the window to the main window */
1454 gw->destination.x = WIN_CENTER_X (main) - (WIN_WIDTH (cw) / 2);
1455 gw->destination.y = WIN_CENTER_Y (main) - (WIN_HEIGHT (cw) / 2);
1456
1457 /* Distance from destination. */
1458 gw->mainTabOffset.x = WIN_X (cw) - gw->destination.x;
1459 gw->mainTabOffset.y = WIN_Y (cw) - gw->destination.y;
1460
1461 if (gw->tx || gw->ty)
1462 {
1463 gw->tx -= (WIN_X (cw) - gw->orgPos.x);
1464 gw->ty -= (WIN_Y (cw) - gw->orgPos.y);
1465 }
1466
1467 gw->orgPos.x = WIN_X (cw);
1468 gw->orgPos.y = WIN_Y (cw);
1469
1470 gw->animateState = IS_ANIMATED;
1471 gw->xVelocity = gw->yVelocity = 0.0f;
1472 }
1473
1474 groupStartTabbingAnimation (group, TRUE);
1475 }
1476
1477 /*
1478 * groupUntabGroup
1479 *
1480 */
1481 void
groupUntabGroup(GroupSelection * group)1482 groupUntabGroup (GroupSelection *group)
1483 {
1484 int oldX, oldY;
1485 CompWindow *prevTopTab;
1486 GroupTabBarSlot *slot;
1487
1488 if (!HAS_TOP_WIN (group))
1489 return;
1490
1491 GROUP_SCREEN (group->screen);
1492
1493 if (group->prevTopTab)
1494 prevTopTab = PREV_TOP_TAB (group);
1495 else
1496 {
1497 /* If prevTopTab isn't set, we have no choice but using topTab.
1498 It happens when there is still animation, which
1499 means the tab wasn't changed anyway. */
1500 prevTopTab = TOP_TAB (group);
1501 }
1502
1503 group->lastTopTab = TOP_TAB (group);
1504 group->topTab = NULL;
1505
1506 for (slot = group->tabBar->slots; slot; slot = slot->next)
1507 {
1508 CompWindow *cw = slot->window;
1509
1510 GROUP_WINDOW (cw);
1511
1512 if (gw->animateState & (IS_ANIMATED | FINISHED_ANIMATION))
1513 {
1514 gs->queued = TRUE;
1515 moveWindow (cw,
1516 gw->destination.x - WIN_X (cw),
1517 gw->destination.y - WIN_Y (cw),
1518 FALSE, TRUE);
1519 gs->queued = FALSE;
1520 }
1521 groupSetWindowVisibility (cw, TRUE);
1522
1523 /* save the old original position - we might need it
1524 if constraining fails */
1525 oldX = gw->orgPos.x;
1526 oldY = gw->orgPos.y;
1527
1528 gw->orgPos.x = WIN_CENTER_X (prevTopTab) - WIN_WIDTH (cw) / 2;
1529 gw->orgPos.y = WIN_CENTER_Y (prevTopTab) - WIN_HEIGHT (cw) / 2;
1530
1531 gw->destination.x = gw->orgPos.x + gw->mainTabOffset.x;
1532 gw->destination.y = gw->orgPos.y + gw->mainTabOffset.y;
1533
1534 if (gw->tx || gw->ty)
1535 {
1536 gw->tx -= (gw->orgPos.x - oldX);
1537 gw->ty -= (gw->orgPos.y - oldY);
1538 }
1539
1540 gw->mainTabOffset.x = oldX;
1541 gw->mainTabOffset.y = oldY;
1542
1543 gw->animateState = IS_ANIMATED;
1544 gw->xVelocity = gw->yVelocity = 0.0f;
1545 }
1546
1547 group->tabbingState = NoTabbing;
1548 groupStartTabbingAnimation (group, FALSE);
1549
1550 groupDeleteTabBar (group);
1551 group->changeAnimationTime = 0;
1552 group->changeState = NoTabChange;
1553 group->nextTopTab = NULL;
1554 group->prevTopTab = NULL;
1555
1556 damageScreen (group->screen);
1557 }
1558
1559 /*
1560 * groupChangeTab
1561 *
1562 */
1563 Bool
groupChangeTab(GroupTabBarSlot * topTab,ChangeTabAnimationDirection direction)1564 groupChangeTab (GroupTabBarSlot *topTab,
1565 ChangeTabAnimationDirection direction)
1566 {
1567 CompWindow *w, *oldTopTab;
1568 GroupSelection *group;
1569 CompScreen *s;
1570
1571 if (!topTab)
1572 return TRUE;
1573
1574 w = topTab->window;
1575 s = w->screen;
1576
1577 GROUP_WINDOW (w);
1578
1579 group = gw->group;
1580
1581 if (!group || group->tabbingState != NoTabbing)
1582 return TRUE;
1583
1584 if (group->changeState == NoTabChange && group->topTab == topTab)
1585 return TRUE;
1586
1587 if (group->changeState != NoTabChange && group->nextTopTab == topTab)
1588 return TRUE;
1589
1590 oldTopTab = group->topTab ? group->topTab->window : NULL;
1591
1592 if (group->changeState != NoTabChange)
1593 group->nextDirection = direction;
1594 else if (direction == RotateLeft)
1595 group->changeAnimationDirection = 1;
1596 else if (direction == RotateRight)
1597 group->changeAnimationDirection = -1;
1598 else
1599 {
1600 int distanceOld = 0, distanceNew = 0;
1601 GroupTabBarSlot *slot;
1602
1603 if (group->topTab)
1604 for (slot = group->tabBar->slots; slot && (slot != group->topTab);
1605 slot = slot->next, distanceOld++);
1606
1607 for (slot = group->tabBar->slots; slot && (slot != topTab);
1608 slot = slot->next, distanceNew++);
1609
1610 if (distanceNew < distanceOld)
1611 group->changeAnimationDirection = 1; /*left */
1612 else
1613 group->changeAnimationDirection = -1; /* right */
1614
1615 /* check if the opposite direction is shorter */
1616 if (abs (distanceNew - distanceOld) > (group->tabBar->nSlots / 2))
1617 group->changeAnimationDirection *= -1;
1618 }
1619
1620 if (group->changeState != NoTabChange)
1621 {
1622 if (group->prevTopTab == topTab)
1623 {
1624 /* Reverse animation. */
1625 GroupTabBarSlot *tmp = group->topTab;
1626 group->topTab = group->prevTopTab;
1627 group->prevTopTab = tmp;
1628
1629 group->changeAnimationDirection *= -1;
1630 group->changeAnimationTime =
1631 groupGetChangeAnimationTime (s) * 500 -
1632 group->changeAnimationTime;
1633 group->changeState = (group->changeState == TabChangeOldOut) ?
1634 TabChangeNewIn : TabChangeOldOut;
1635
1636 group->nextTopTab = NULL;
1637 }
1638 else
1639 group->nextTopTab = topTab;
1640 }
1641 else
1642 {
1643 group->topTab = topTab;
1644
1645 groupRenderWindowTitle (group);
1646 groupRenderTopTabHighlight (group);
1647 if (oldTopTab)
1648 addWindowDamage (oldTopTab);
1649 addWindowDamage (w);
1650 }
1651
1652 if (topTab != group->nextTopTab)
1653 {
1654 groupSetWindowVisibility (w, TRUE);
1655 if (oldTopTab)
1656 {
1657 int dx, dy;
1658
1659 GROUP_SCREEN (s);
1660
1661 dx = WIN_CENTER_X (oldTopTab) - WIN_CENTER_X (w);
1662 dy = WIN_CENTER_Y (oldTopTab) - WIN_CENTER_Y (w);
1663
1664 gs->queued = TRUE;
1665 moveWindow (w, dx, dy, FALSE, TRUE);
1666 syncWindowPosition (w);
1667 gs->queued = FALSE;
1668 }
1669
1670 if (HAS_PREV_TOP_WIN (group))
1671 {
1672 /* we use only the half time here -
1673 the second half will be PaintFadeOut */
1674 group->changeAnimationTime =
1675 groupGetChangeAnimationTime (s) * 500;
1676 groupTabChangeActivateEvent (s, TRUE);
1677 group->changeState = TabChangeOldOut;
1678 }
1679 else
1680 {
1681 Bool activate;
1682
1683 /* No window to do animation with. */
1684 if (HAS_TOP_WIN (group))
1685 group->prevTopTab = group->topTab;
1686 else
1687 group->prevTopTab = NULL;
1688
1689 activate = !group->checkFocusAfterTabChange;
1690 if (!activate)
1691 {
1692 CompFocusResult focus;
1693
1694 focus = allowWindowFocus (w, NO_FOCUS_MASK, s->x, s->y, 0);
1695 activate = focus == CompFocusAllowed;
1696 }
1697
1698 if (activate)
1699 (*s->activateWindow) (w);
1700
1701 group->checkFocusAfterTabChange = FALSE;
1702 }
1703 }
1704
1705 return TRUE;
1706 }
1707
1708 /*
1709 * groupRecalcSlotPos
1710 *
1711 */
1712 static void
groupRecalcSlotPos(GroupTabBarSlot * slot,int slotPos)1713 groupRecalcSlotPos (GroupTabBarSlot *slot,
1714 int slotPos)
1715 {
1716 GroupSelection *group;
1717 XRectangle box;
1718 int space, thumbSize;
1719
1720 GROUP_WINDOW (slot->window);
1721 group = gw->group;
1722
1723 if (!HAS_TOP_WIN (group) || !group->tabBar)
1724 return;
1725
1726 space = groupGetThumbSpace (slot->window->screen);
1727 thumbSize = groupGetThumbSize (slot->window->screen);
1728
1729 EMPTY_REGION (slot->region);
1730
1731 box.x = space + ((thumbSize + space) * slotPos);
1732 box.y = space;
1733
1734 box.width = thumbSize;
1735 box.height = thumbSize;
1736
1737 XUnionRectWithRegion (&box, slot->region, slot->region);
1738 }
1739
1740 /*
1741 * groupRecalcTabBarPos
1742 *
1743 */
1744 void
groupRecalcTabBarPos(GroupSelection * group,int middleX,int minX1,int maxX2)1745 groupRecalcTabBarPos (GroupSelection *group,
1746 int middleX,
1747 int minX1,
1748 int maxX2)
1749 {
1750 GroupTabBarSlot *slot;
1751 GroupTabBar *bar;
1752 CompWindow *topTab;
1753 Bool isDraggedSlotGroup = FALSE;
1754 int space, barWidth;
1755 int thumbSize;
1756 int tabsWidth = 0, tabsHeight = 0;
1757 int currentSlot;
1758 XRectangle box;
1759
1760 if (!HAS_TOP_WIN (group) || !group->tabBar)
1761 return;
1762
1763 GROUP_SCREEN (group->screen);
1764
1765 bar = group->tabBar;
1766 topTab = TOP_TAB (group);
1767 space = groupGetThumbSpace (group->screen);
1768
1769 /* calculate the space which the tabs need */
1770 for (slot = bar->slots; slot; slot = slot->next)
1771 {
1772 if (slot == gs->draggedSlot && gs->dragged)
1773 {
1774 isDraggedSlotGroup = TRUE;
1775 continue;
1776 }
1777
1778 tabsWidth += (slot->region->extents.x2 - slot->region->extents.x1);
1779 if ((slot->region->extents.y2 - slot->region->extents.y1) > tabsHeight)
1780 tabsHeight = slot->region->extents.y2 - slot->region->extents.y1;
1781 }
1782
1783 /* just a little work-a-round for first call
1784 FIXME: remove this! */
1785 thumbSize = groupGetThumbSize (group->screen);
1786 if (bar->nSlots && tabsWidth <= 0)
1787 {
1788 /* first call */
1789 tabsWidth = thumbSize * bar->nSlots;
1790
1791 if (bar->nSlots && tabsHeight < thumbSize)
1792 {
1793 /* we need to do the standard height too */
1794 tabsHeight = thumbSize;
1795 }
1796
1797 if (isDraggedSlotGroup)
1798 tabsWidth -= thumbSize;
1799 }
1800
1801 barWidth = space * (bar->nSlots + 1) + tabsWidth;
1802
1803 if (isDraggedSlotGroup)
1804 {
1805 /* 1 tab is missing, so we have 1 less border */
1806 barWidth -= space;
1807 }
1808
1809 if (maxX2 - minX1 < barWidth)
1810 box.x = (maxX2 + minX1) / 2 - barWidth / 2;
1811 else if (middleX - barWidth / 2 < minX1)
1812 box.x = minX1;
1813 else if (middleX + barWidth / 2 > maxX2)
1814 box.x = maxX2 - barWidth;
1815 else
1816 box.x = middleX - barWidth / 2;
1817
1818 box.y = WIN_Y (topTab);
1819 box.width = barWidth;
1820 box.height = space * 2 + tabsHeight;
1821
1822 groupResizeTabBarRegion (group, &box, TRUE);
1823
1824 /* recalc every slot region */
1825 currentSlot = 0;
1826 for (slot = bar->slots; slot; slot = slot->next)
1827 {
1828 if (slot == gs->draggedSlot && gs->dragged)
1829 continue;
1830
1831 groupRecalcSlotPos (slot, currentSlot);
1832 XOffsetRegion (slot->region,
1833 bar->region->extents.x1,
1834 bar->region->extents.y1);
1835
1836 slot->springX = (slot->region->extents.x1 +
1837 slot->region->extents.x2) / 2;
1838 slot->speed = 0;
1839 slot->msSinceLastMove = 0;
1840
1841 currentSlot++;
1842 }
1843
1844 bar->leftSpringX = box.x;
1845 bar->rightSpringX = box.x + box.width;
1846
1847 bar->rightSpeed = 0;
1848 bar->leftSpeed = 0;
1849
1850 bar->rightMsSinceLastMove = 0;
1851 bar->leftMsSinceLastMove = 0;
1852 }
1853
1854 void
groupDamageTabBarRegion(GroupSelection * group)1855 groupDamageTabBarRegion (GroupSelection *group)
1856 {
1857 REGION reg;
1858
1859 reg.rects = ®.extents;
1860 reg.numRects = 1;
1861
1862 /* we use 15 pixels as damage buffer here, as there is a 10 pixel wide
1863 border around the selected slot which also needs to be damaged
1864 properly - however the best way would be if slot->region was
1865 sized including the border */
1866
1867 #define DAMAGE_BUFFER 20
1868
1869 reg.extents = group->tabBar->region->extents;
1870
1871 if (group->tabBar->slots)
1872 {
1873 reg.extents.x1 = MIN (reg.extents.x1,
1874 group->tabBar->slots->region->extents.x1);
1875 reg.extents.y1 = MIN (reg.extents.y1,
1876 group->tabBar->slots->region->extents.y1);
1877 reg.extents.x2 = MAX (reg.extents.x2,
1878 group->tabBar->revSlots->region->extents.x2);
1879 reg.extents.y2 = MAX (reg.extents.y2,
1880 group->tabBar->revSlots->region->extents.y2);
1881 }
1882
1883 reg.extents.x1 -= DAMAGE_BUFFER;
1884 reg.extents.y1 -= DAMAGE_BUFFER;
1885 reg.extents.x2 += DAMAGE_BUFFER;
1886 reg.extents.y2 += DAMAGE_BUFFER;
1887
1888 damageScreenRegion (group->screen, ®);
1889 }
1890
1891 void
groupMoveTabBarRegion(GroupSelection * group,int dx,int dy,Bool syncIPW)1892 groupMoveTabBarRegion (GroupSelection *group,
1893 int dx,
1894 int dy,
1895 Bool syncIPW)
1896 {
1897 groupDamageTabBarRegion (group);
1898
1899 XOffsetRegion (group->tabBar->region, dx, dy);
1900
1901 if (syncIPW)
1902 XMoveWindow (group->screen->display->display,
1903 group->inputPrevention,
1904 group->tabBar->leftSpringX,
1905 group->tabBar->region->extents.y1);
1906
1907 groupDamageTabBarRegion (group);
1908 }
1909
1910 void
groupResizeTabBarRegion(GroupSelection * group,XRectangle * box,Bool syncIPW)1911 groupResizeTabBarRegion (GroupSelection *group,
1912 XRectangle *box,
1913 Bool syncIPW)
1914 {
1915 int oldWidth;
1916
1917 groupDamageTabBarRegion (group);
1918
1919 oldWidth = group->tabBar->region->extents.x2 -
1920 group->tabBar->region->extents.x1;
1921
1922 if (group->tabBar->bgLayer && oldWidth != box->width && syncIPW)
1923 {
1924 group->tabBar->bgLayer =
1925 groupRebuildCairoLayer (group->screen,
1926 group->tabBar->bgLayer,
1927 box->width +
1928 groupGetThumbSpace (group->screen) +
1929 groupGetThumbSize (group->screen),
1930 box->height);
1931 groupRenderTabBarBackground (group);
1932
1933 /* invalidate old width */
1934 group->tabBar->oldWidth = 0;
1935 }
1936
1937 EMPTY_REGION (group->tabBar->region);
1938 XUnionRectWithRegion (box, group->tabBar->region, group->tabBar->region);
1939
1940 if (syncIPW)
1941 {
1942 XWindowChanges xwc;
1943
1944 xwc.x = box->x;
1945 xwc.y = box->y;
1946 xwc.width = box->width;
1947 xwc.height = box->height;
1948
1949 xwc.stack_mode = Above;
1950 xwc.sibling = HAS_TOP_WIN (group) ? TOP_TAB (group)->id : None;
1951
1952 XConfigureWindow (group->screen->display->display,
1953 group->inputPrevention,
1954 CWSibling | CWStackMode | CWX | CWY |
1955 CWWidth | CWHeight,
1956 &xwc);
1957 }
1958
1959 groupDamageTabBarRegion (group);
1960 }
1961
1962 /*
1963 * groupInsertTabBarSlotBefore
1964 *
1965 */
1966 void
groupInsertTabBarSlotBefore(GroupTabBar * bar,GroupTabBarSlot * slot,GroupTabBarSlot * nextSlot)1967 groupInsertTabBarSlotBefore (GroupTabBar *bar,
1968 GroupTabBarSlot *slot,
1969 GroupTabBarSlot *nextSlot)
1970 {
1971 GroupTabBarSlot *prev = nextSlot->prev;
1972 CompWindow *w = slot->window;
1973
1974 GROUP_WINDOW (w);
1975
1976 if (prev)
1977 {
1978 slot->prev = prev;
1979 prev->next = slot;
1980 }
1981 else
1982 {
1983 bar->slots = slot;
1984 slot->prev = NULL;
1985 }
1986
1987 slot->next = nextSlot;
1988 nextSlot->prev = slot;
1989 bar->nSlots++;
1990
1991 /* Moving bar->region->extents.x1 / x2 as minX1 / maxX2 will work,
1992 because the tab-bar got wider now, so it will put it in
1993 the average between them, which is
1994 (bar->region->extents.x1 + bar->region->extents.x2) / 2 anyway. */
1995 groupRecalcTabBarPos (gw->group,
1996 (bar->region->extents.x1 +
1997 bar->region->extents.x2) / 2,
1998 bar->region->extents.x1, bar->region->extents.x2);
1999 }
2000
2001 /*
2002 * groupInsertTabBarSlotAfter
2003 *
2004 */
2005 void
groupInsertTabBarSlotAfter(GroupTabBar * bar,GroupTabBarSlot * slot,GroupTabBarSlot * prevSlot)2006 groupInsertTabBarSlotAfter (GroupTabBar *bar,
2007 GroupTabBarSlot *slot,
2008 GroupTabBarSlot *prevSlot)
2009 {
2010 GroupTabBarSlot *next = prevSlot->next;
2011 CompWindow *w = slot->window;
2012
2013 GROUP_WINDOW (w);
2014
2015 if (next)
2016 {
2017 slot->next = next;
2018 next->prev = slot;
2019 }
2020 else
2021 {
2022 bar->revSlots = slot;
2023 slot->next = NULL;
2024 }
2025
2026 slot->prev = prevSlot;
2027 prevSlot->next = slot;
2028 bar->nSlots++;
2029
2030 /* Moving bar->region->extents.x1 / x2 as minX1 / maxX2 will work,
2031 because the tab-bar got wider now, so it will put it in the
2032 average between them, which is
2033 (bar->region->extents.x1 + bar->region->extents.x2) / 2 anyway. */
2034 groupRecalcTabBarPos (gw->group,
2035 (bar->region->extents.x1 +
2036 bar->region->extents.x2) / 2,
2037 bar->region->extents.x1, bar->region->extents.x2);
2038 }
2039
2040 /*
2041 * groupInsertTabBarSlot
2042 *
2043 */
2044 void
groupInsertTabBarSlot(GroupTabBar * bar,GroupTabBarSlot * slot)2045 groupInsertTabBarSlot (GroupTabBar *bar,
2046 GroupTabBarSlot *slot)
2047 {
2048 CompWindow *w = slot->window;
2049
2050 GROUP_WINDOW (w);
2051
2052 if (bar->slots)
2053 {
2054 bar->revSlots->next = slot;
2055 slot->prev = bar->revSlots;
2056 slot->next = NULL;
2057 }
2058 else
2059 {
2060 slot->prev = NULL;
2061 slot->next = NULL;
2062 bar->slots = slot;
2063 }
2064
2065 bar->revSlots = slot;
2066 bar->nSlots++;
2067
2068 /* Moving bar->region->extents.x1 / x2 as minX1 / maxX2 will work,
2069 because the tab-bar got wider now, so it will put it in
2070 the average between them, which is
2071 (bar->region->extents.x1 + bar->region->extents.x2) / 2 anyway. */
2072 groupRecalcTabBarPos (gw->group,
2073 (bar->region->extents.x1 +
2074 bar->region->extents.x2) / 2,
2075 bar->region->extents.x1, bar->region->extents.x2);
2076 }
2077
2078 /*
2079 * groupUnhookTabBarSlot
2080 *
2081 */
2082 void
groupUnhookTabBarSlot(GroupTabBar * bar,GroupTabBarSlot * slot,Bool temporary)2083 groupUnhookTabBarSlot (GroupTabBar *bar,
2084 GroupTabBarSlot *slot,
2085 Bool temporary)
2086 {
2087 GroupTabBarSlot *tempSlot;
2088 GroupTabBarSlot *prev = slot->prev;
2089 GroupTabBarSlot *next = slot->next;
2090 CompWindow *w = slot->window;
2091 CompScreen *s = w->screen;
2092 GroupSelection *group;
2093
2094 GROUP_WINDOW (w);
2095
2096 group = gw->group;
2097
2098 /* check if slot is not already unhooked */
2099 for (tempSlot = bar->slots; tempSlot; tempSlot = tempSlot->next)
2100 if (tempSlot == slot)
2101 break;
2102
2103 if (!tempSlot)
2104 return;
2105
2106 if (prev)
2107 prev->next = next;
2108 else
2109 bar->slots = next;
2110
2111 if (next)
2112 next->prev = prev;
2113 else
2114 bar->revSlots = prev;
2115
2116 slot->prev = NULL;
2117 slot->next = NULL;
2118 bar->nSlots--;
2119
2120 if (!temporary)
2121 {
2122 if (IS_PREV_TOP_TAB (w, group))
2123 group->prevTopTab = NULL;
2124 if (IS_TOP_TAB (w, group))
2125 {
2126 group->topTab = NULL;
2127
2128 if (next)
2129 groupChangeTab (next, RotateRight);
2130 else if (prev)
2131 groupChangeTab (prev, RotateLeft);
2132
2133 if (groupGetUntabOnClose (s))
2134 groupUntabGroup (group);
2135 }
2136 }
2137
2138 if (slot == bar->hoveredSlot)
2139 bar->hoveredSlot = NULL;
2140
2141 if (slot == bar->textSlot)
2142 {
2143 bar->textSlot = NULL;
2144
2145 if (bar->textLayer)
2146 {
2147 if (bar->textLayer->state == PaintFadeIn ||
2148 bar->textLayer->state == PaintOn)
2149 {
2150 bar->textLayer->animationTime =
2151 (groupGetFadeTextTime (s) * 1000) -
2152 bar->textLayer->animationTime;
2153 bar->textLayer->state = PaintFadeOut;
2154 }
2155 }
2156 }
2157
2158 /* Moving bar->region->extents.x1 / x2 as minX1 / maxX2 will work,
2159 because the tab-bar got thiner now, so
2160 (bar->region->extents.x1 + bar->region->extents.x2) / 2
2161 Won't cause the new x1 / x2 to be outside the original region. */
2162 groupRecalcTabBarPos (group,
2163 (bar->region->extents.x1 +
2164 bar->region->extents.x2) / 2,
2165 bar->region->extents.x1,
2166 bar->region->extents.x2);
2167 }
2168
2169 /*
2170 * groupDeleteTabBarSlot
2171 *
2172 */
2173 void
groupDeleteTabBarSlot(GroupTabBar * bar,GroupTabBarSlot * slot)2174 groupDeleteTabBarSlot (GroupTabBar *bar,
2175 GroupTabBarSlot *slot)
2176 {
2177 CompWindow *w = slot->window;
2178
2179 GROUP_WINDOW (w);
2180 GROUP_SCREEN (w->screen);
2181
2182 groupUnhookTabBarSlot (bar, slot, FALSE);
2183
2184 if (slot->region)
2185 XDestroyRegion (slot->region);
2186
2187 if (slot == gs->draggedSlot)
2188 {
2189 gs->draggedSlot = NULL;
2190 gs->dragged = FALSE;
2191
2192 if (gs->grabState == ScreenGrabTabDrag)
2193 groupGrabScreen (w->screen, ScreenGrabNone);
2194 }
2195
2196 gw->slot = NULL;
2197 groupUpdateWindowProperty (w);
2198 free (slot);
2199 }
2200
2201 /*
2202 * groupCreateSlot
2203 *
2204 */
groupCreateSlot(GroupSelection * group,CompWindow * w)2205 void groupCreateSlot (GroupSelection *group,
2206 CompWindow *w)
2207 {
2208 GroupTabBarSlot *slot;
2209
2210 GROUP_WINDOW (w);
2211
2212 if (!group->tabBar)
2213 return;
2214
2215 slot = malloc (sizeof (GroupTabBarSlot));
2216 if (!slot)
2217 return;
2218
2219 slot->window = w;
2220
2221 slot->region = XCreateRegion ();
2222
2223 groupInsertTabBarSlot (group->tabBar, slot);
2224 gw->slot = slot;
2225 groupUpdateWindowProperty (w);
2226 }
2227
2228 #define SPRING_K groupGetDragSpringK(s)
2229 #define FRICTION groupGetDragFriction(s)
2230 #define SIZE groupGetThumbSize(s)
2231 #define BORDER groupGetBorderRadius(s)
2232 #define Y_START_MOVE groupGetDragYDistance(s)
2233 #define SPEED_LIMIT groupGetDragSpeedLimit(s)
2234
2235 /*
2236 * groupSpringForce
2237 *
2238 */
2239 static inline int
groupSpringForce(CompScreen * s,int centerX,int springX)2240 groupSpringForce (CompScreen *s,
2241 int centerX,
2242 int springX)
2243 {
2244 /* Each slot has a spring attached to it, starting at springX,
2245 and ending at the center of the slot (centerX).
2246 The spring will cause the slot to move, using the
2247 well-known physical formula F = k * dl... */
2248 return -SPRING_K * (centerX - springX);
2249 }
2250
2251 /*
2252 * groupDraggedSlotForce
2253 *
2254 */
2255 static int
groupDraggedSlotForce(CompScreen * s,int distanceX,int distanceY)2256 groupDraggedSlotForce (CompScreen *s,
2257 int distanceX,
2258 int distanceY)
2259 {
2260 /* The dragged slot will make the slot move, to get
2261 DnD animations (slots will make room for the newly inserted slot).
2262 As the dragged slot is closer to the slot, it will put
2263 more force on the slot, causing it to make room for the dragged slot...
2264 But if the dragged slot gets too close to the slot, they are
2265 going to be reordered soon, so the force will get lower.
2266
2267 If the dragged slot is in the other side of the slot,
2268 it will have to make force in the opposite direction.
2269
2270 So we the needed funtion is an odd function that goes
2271 up at first, and down after that.
2272 Sinus is a function like that... :)
2273
2274 The maximum is got when x = (x1 + x2) / 2,
2275 in this case: x = SIZE + BORDER.
2276 Because of that, for x = SIZE + BORDER,
2277 we get a force of SPRING_K * (SIZE + BORDER) / 2.
2278 That equals to the force we get from the the spring.
2279 This way, the slot won't move when its distance from
2280 the dragged slot is SIZE + BORDER (which is the default
2281 distance between slots).
2282 */
2283
2284 /* The maximum value */
2285 float a = SPRING_K * (SIZE + BORDER) / 2;
2286 /* This will make distanceX == 2 * (SIZE + BORDER) to get 0,
2287 and distanceX == (SIZE + BORDER) to get the maximum. */
2288 float b = PI / (2 * SIZE + 2 * BORDER);
2289
2290 /* If there is some distance between the slots in the y axis,
2291 the slot should get less force... For this, we change max
2292 to a lower value, using a simple linear function. */
2293
2294 if (distanceY < Y_START_MOVE)
2295 a *= 1.0f - (float)distanceY / Y_START_MOVE;
2296 else
2297 a = 0;
2298
2299 if (abs (distanceX) < 2 * (SIZE + BORDER))
2300 return a * sin (b * distanceX);
2301 else
2302 return 0;
2303 }
2304
2305 /*
2306 * groupApplyFriction
2307 *
2308 */
2309 static inline void
groupApplyFriction(CompScreen * s,int * speed)2310 groupApplyFriction (CompScreen *s,
2311 int *speed)
2312 {
2313 if (abs (*speed) < FRICTION)
2314 *speed = 0;
2315 else if (*speed > 0)
2316 *speed -= FRICTION;
2317 else if (*speed < 0)
2318 *speed += FRICTION;
2319 }
2320
2321 /*
2322 * groupApplySpeedLimit
2323 *
2324 */
2325 static inline void
groupApplySpeedLimit(CompScreen * s,int * speed)2326 groupApplySpeedLimit (CompScreen *s,
2327 int *speed)
2328 {
2329 if (*speed > SPEED_LIMIT)
2330 *speed = SPEED_LIMIT;
2331 else if (*speed < -SPEED_LIMIT)
2332 *speed = -SPEED_LIMIT;
2333 }
2334
2335 /*
2336 * groupApplyForces
2337 *
2338 */
2339 void
groupApplyForces(CompScreen * s,GroupTabBar * bar,GroupTabBarSlot * draggedSlot)2340 groupApplyForces (CompScreen *s,
2341 GroupTabBar *bar,
2342 GroupTabBarSlot *draggedSlot)
2343 {
2344 GroupTabBarSlot *slot, *slot2;
2345 int centerX, centerY;
2346 int draggedCenterX, draggedCenterY;
2347
2348 if (draggedSlot)
2349 {
2350 int vx, vy;
2351
2352 groupGetDrawOffsetForSlot (draggedSlot, &vx, &vy);
2353
2354 draggedCenterX = ((draggedSlot->region->extents.x1 +
2355 draggedSlot->region->extents.x2) / 2) + vx;
2356 draggedCenterY = ((draggedSlot->region->extents.y1 +
2357 draggedSlot->region->extents.y2) / 2) + vy;
2358 }
2359 else
2360 {
2361 draggedCenterX = 0;
2362 draggedCenterY = 0;
2363 }
2364
2365 bar->leftSpeed += groupSpringForce(s,
2366 bar->region->extents.x1,
2367 bar->leftSpringX);
2368 bar->rightSpeed += groupSpringForce(s,
2369 bar->region->extents.x2,
2370 bar->rightSpringX);
2371
2372 if (draggedSlot)
2373 {
2374 int leftForce, rightForce;
2375
2376 leftForce = groupDraggedSlotForce(s,
2377 bar->region->extents.x1 -
2378 SIZE / 2 - draggedCenterX,
2379 abs ((bar->region->extents.y1 +
2380 bar->region->extents.y2) / 2 -
2381 draggedCenterY));
2382
2383 rightForce = groupDraggedSlotForce (s,
2384 bar->region->extents.x2 +
2385 SIZE / 2 - draggedCenterX,
2386 abs ((bar->region->extents.y1 +
2387 bar->region->extents.y2) / 2 -
2388 draggedCenterY));
2389
2390 if (leftForce < 0)
2391 bar->leftSpeed += leftForce;
2392 if (rightForce > 0)
2393 bar->rightSpeed += rightForce;
2394 }
2395
2396 for (slot = bar->slots; slot; slot = slot->next)
2397 {
2398 centerX = (slot->region->extents.x1 + slot->region->extents.x2) / 2;
2399 centerY = (slot->region->extents.y1 + slot->region->extents.y2) / 2;
2400
2401 slot->speed += groupSpringForce (s, centerX, slot->springX);
2402
2403 if (draggedSlot && draggedSlot != slot)
2404 {
2405 int draggedSlotForce;
2406 draggedSlotForce =
2407 groupDraggedSlotForce(s, centerX - draggedCenterX,
2408 abs (centerY - draggedCenterY));
2409
2410 slot->speed += draggedSlotForce;
2411 slot2 = NULL;
2412
2413 if (draggedSlotForce < 0)
2414 {
2415 slot2 = slot->prev;
2416 bar->leftSpeed += draggedSlotForce;
2417 }
2418 else if (draggedSlotForce > 0)
2419 {
2420 slot2 = slot->next;
2421 bar->rightSpeed += draggedSlotForce;
2422 }
2423
2424 while (slot2)
2425 {
2426 if (slot2 != draggedSlot)
2427 slot2->speed += draggedSlotForce;
2428
2429 slot2 = (draggedSlotForce < 0) ? slot2->prev : slot2->next;
2430 }
2431 }
2432 }
2433
2434 for (slot = bar->slots; slot; slot = slot->next)
2435 {
2436 groupApplyFriction (s, &slot->speed);
2437 groupApplySpeedLimit (s, &slot->speed);
2438 }
2439
2440 groupApplyFriction (s, &bar->leftSpeed);
2441 groupApplySpeedLimit (s, &bar->leftSpeed);
2442
2443 groupApplyFriction (s, &bar->rightSpeed);
2444 groupApplySpeedLimit (s, &bar->rightSpeed);
2445 }
2446
2447 /*
2448 * groupApplySpeeds
2449 *
2450 */
2451 void
groupApplySpeeds(CompScreen * s,GroupSelection * group,int msSinceLastRepaint)2452 groupApplySpeeds (CompScreen *s,
2453 GroupSelection *group,
2454 int msSinceLastRepaint)
2455 {
2456 GroupTabBar *bar = group->tabBar;
2457 GroupTabBarSlot *slot;
2458 int move;
2459 XRectangle box;
2460 Bool updateTabBar = FALSE;
2461
2462 box.x = bar->region->extents.x1;
2463 box.y = bar->region->extents.y1;
2464 box.width = bar->region->extents.x2 - bar->region->extents.x1;
2465 box.height = bar->region->extents.y2 - bar->region->extents.y1;
2466
2467 bar->leftMsSinceLastMove += msSinceLastRepaint;
2468 bar->rightMsSinceLastMove += msSinceLastRepaint;
2469
2470 /* Left */
2471 move = bar->leftSpeed * bar->leftMsSinceLastMove / 1000;
2472 if (move)
2473 {
2474 box.x += move;
2475 box.width -= move;
2476
2477 bar->leftMsSinceLastMove = 0;
2478 updateTabBar = TRUE;
2479 }
2480 else if (bar->leftSpeed == 0 &&
2481 bar->region->extents.x1 != bar->leftSpringX &&
2482 (SPRING_K * abs (bar->region->extents.x1 - bar->leftSpringX) <
2483 FRICTION))
2484 {
2485 /* Friction is preventing from the left border to get
2486 to its original position. */
2487 box.x += bar->leftSpringX - bar->region->extents.x1;
2488 box.width -= bar->leftSpringX - bar->region->extents.x1;
2489
2490 bar->leftMsSinceLastMove = 0;
2491 updateTabBar = TRUE;
2492 }
2493 else if (bar->leftSpeed == 0)
2494 bar->leftMsSinceLastMove = 0;
2495
2496 /* Right */
2497 move = bar->rightSpeed * bar->rightMsSinceLastMove / 1000;
2498 if (move)
2499 {
2500 box.width += move;
2501
2502 bar->rightMsSinceLastMove = 0;
2503 updateTabBar = TRUE;
2504 }
2505 else if (bar->rightSpeed == 0 &&
2506 bar->region->extents.x2 != bar->rightSpringX &&
2507 (SPRING_K * abs (bar->region->extents.x2 - bar->rightSpringX) <
2508 FRICTION))
2509 {
2510 /* Friction is preventing from the right border to get
2511 to its original position. */
2512 box.width += bar->leftSpringX - bar->region->extents.x1;
2513
2514 bar->leftMsSinceLastMove = 0;
2515 updateTabBar = TRUE;
2516 }
2517 else if (bar->rightSpeed == 0)
2518 bar->rightMsSinceLastMove = 0;
2519
2520 if (updateTabBar)
2521 groupResizeTabBarRegion (group, &box, FALSE);
2522
2523 for (slot = bar->slots; slot; slot = slot->next)
2524 {
2525 int slotCenter;
2526
2527 slot->msSinceLastMove += msSinceLastRepaint;
2528 move = slot->speed * slot->msSinceLastMove / 1000;
2529 slotCenter = (slot->region->extents.x1 +
2530 slot->region->extents.x2) / 2;
2531
2532 if (move)
2533 {
2534 XOffsetRegion (slot->region, move, 0);
2535 slot->msSinceLastMove = 0;
2536 }
2537 else if (slot->speed == 0 &&
2538 slotCenter != slot->springX &&
2539 SPRING_K * abs (slotCenter - slot->springX) < FRICTION)
2540 {
2541 /* Friction is preventing from the slot to get
2542 to its original position. */
2543
2544 XOffsetRegion (slot->region, slot->springX - slotCenter, 0);
2545 slot->msSinceLastMove = 0;
2546 }
2547 else if (slot->speed == 0)
2548 slot->msSinceLastMove = 0;
2549 }
2550 }
2551
2552 /*
2553 * groupInitTabBar
2554 *
2555 */
2556 void
groupInitTabBar(GroupSelection * group,CompWindow * topTab)2557 groupInitTabBar (GroupSelection *group,
2558 CompWindow *topTab)
2559 {
2560 GroupTabBar *bar;
2561 int i;
2562
2563 if (group->tabBar)
2564 return;
2565
2566 bar = malloc (sizeof (GroupTabBar));
2567 if (!bar)
2568 return;
2569
2570 bar->slots = NULL;
2571 bar->nSlots = 0;
2572 bar->bgAnimation = AnimationNone;
2573 bar->bgAnimationTime = 0;
2574 bar->state = PaintOff;
2575 bar->animationTime = 0;
2576 bar->timeoutHandle = 0;
2577 bar->textLayer = NULL;
2578 bar->bgLayer = NULL;
2579 bar->selectionLayer = NULL;
2580 bar->hoveredSlot = NULL;
2581 bar->textSlot = NULL;
2582 bar->oldWidth = 0;
2583 group->tabBar = bar;
2584
2585 bar->region = XCreateRegion ();
2586
2587 for (i = 0; i < group->nWins; i++)
2588 groupCreateSlot (group, group->windows[i]);
2589
2590 groupRecalcTabBarPos (group, WIN_CENTER_X (topTab),
2591 WIN_X (topTab), WIN_X (topTab) + WIN_WIDTH (topTab));
2592 }
2593
2594 /*
2595 * groupDeleteTabBar
2596 *
2597 */
2598 void
groupDeleteTabBar(GroupSelection * group)2599 groupDeleteTabBar (GroupSelection *group)
2600 {
2601 GroupTabBar *bar = group->tabBar;
2602
2603 groupDestroyCairoLayer (group->screen, bar->textLayer);
2604 groupDestroyCairoLayer (group->screen, bar->bgLayer);
2605 groupDestroyCairoLayer (group->screen, bar->selectionLayer);
2606
2607 groupDestroyInputPreventionWindow (group);
2608
2609 if (bar->timeoutHandle)
2610 compRemoveTimeout (bar->timeoutHandle);
2611
2612 while (bar->slots)
2613 groupDeleteTabBarSlot (bar, bar->slots);
2614
2615 if (bar->region)
2616 XDestroyRegion (bar->region);
2617
2618 free (bar);
2619 group->tabBar = NULL;
2620 }
2621
2622 /*
2623 * groupInitTab
2624 *
2625 */
2626 Bool
groupInitTab(CompDisplay * d,CompAction * action,CompActionState state,CompOption * option,int nOption)2627 groupInitTab (CompDisplay *d,
2628 CompAction *action,
2629 CompActionState state,
2630 CompOption *option,
2631 int nOption)
2632 {
2633 Window xid;
2634 CompWindow *w;
2635 Bool allowUntab = TRUE;
2636
2637 xid = getIntOptionNamed (option, nOption, "window", 0);
2638 w = findWindowAtDisplay (d, xid);
2639 if (!w)
2640 return TRUE;
2641
2642 GROUP_WINDOW (w);
2643
2644 if (gw->inSelection)
2645 {
2646 groupGroupWindows (d, action, state, option, nOption);
2647 /* If the window was selected, we don't want to
2648 untab the group, because the user probably
2649 wanted to tab the selected windows. */
2650 allowUntab = FALSE;
2651 }
2652
2653 if (!gw->group)
2654 return TRUE;
2655
2656 if (!gw->group->tabBar)
2657 groupTabGroup (w);
2658 else if (allowUntab)
2659 groupUntabGroup (gw->group);
2660
2661 damageScreen (w->screen);
2662
2663 return TRUE;
2664 }
2665
2666 /*
2667 * groupChangeTabLeft
2668 *
2669 */
2670 Bool
groupChangeTabLeft(CompDisplay * d,CompAction * action,CompActionState state,CompOption * option,int nOption)2671 groupChangeTabLeft (CompDisplay *d,
2672 CompAction *action,
2673 CompActionState state,
2674 CompOption *option,
2675 int nOption)
2676 {
2677 Window xid;
2678 CompWindow *w, *topTab;
2679
2680 xid = getIntOptionNamed (option, nOption, "window", 0);
2681 w = topTab = findWindowAtDisplay (d, xid);
2682 if (!w)
2683 return TRUE;
2684
2685 GROUP_WINDOW (w);
2686 GROUP_SCREEN (w->screen);
2687
2688 if (!gw->slot || !gw->group)
2689 return TRUE;
2690
2691 if (gw->group->nextTopTab)
2692 topTab = NEXT_TOP_TAB (gw->group);
2693 else if (gw->group->topTab)
2694 {
2695 /* If there are no tabbing animations,
2696 topTab is never NULL. */
2697 topTab = TOP_TAB (gw->group);
2698 }
2699
2700 gw = GET_GROUP_WINDOW (topTab, gs);
2701
2702 if (gw->slot->prev)
2703 return groupChangeTab (gw->slot->prev, RotateLeft);
2704 else
2705 return groupChangeTab (gw->group->tabBar->revSlots, RotateLeft);
2706 }
2707
2708 /*
2709 * groupChangeTabRight
2710 *
2711 */
2712 Bool
groupChangeTabRight(CompDisplay * d,CompAction * action,CompActionState state,CompOption * option,int nOption)2713 groupChangeTabRight (CompDisplay *d,
2714 CompAction *action,
2715 CompActionState state,
2716 CompOption *option,
2717 int nOption)
2718 {
2719 Window xid;
2720 CompWindow *w, *topTab;
2721
2722 xid = getIntOptionNamed (option, nOption, "window", 0);
2723 w = topTab = findWindowAtDisplay (d, xid);
2724 if (!w)
2725 return TRUE;
2726
2727 GROUP_WINDOW (w);
2728 GROUP_SCREEN (w->screen);
2729
2730 if (!gw->slot || !gw->group)
2731 return TRUE;
2732
2733 if (gw->group->nextTopTab)
2734 topTab = NEXT_TOP_TAB (gw->group);
2735 else if (gw->group->topTab)
2736 {
2737 /* If there are no tabbing animations,
2738 topTab is never NULL. */
2739 topTab = TOP_TAB (gw->group);
2740 }
2741
2742 gw = GET_GROUP_WINDOW (topTab, gs);
2743
2744 if (gw->slot->next)
2745 return groupChangeTab (gw->slot->next, RotateRight);
2746 else
2747 return groupChangeTab (gw->group->tabBar->slots, RotateRight);
2748 }
2749
2750 /*
2751 * groupSwitchTopTabInput
2752 *
2753 */
2754 void
groupSwitchTopTabInput(GroupSelection * group,Bool enable)2755 groupSwitchTopTabInput (GroupSelection *group,
2756 Bool enable)
2757 {
2758 if (!group->tabBar || !HAS_TOP_WIN (group))
2759 return;
2760
2761 if (!group->inputPrevention)
2762 groupCreateInputPreventionWindow (group);
2763
2764 if (!enable)
2765 XMapWindow (group->screen->display->display,
2766 group->inputPrevention);
2767 else
2768 XUnmapWindow (group->screen->display->display,
2769 group->inputPrevention);
2770
2771 group->ipwMapped = !enable;
2772 }
2773
2774 /*
2775 * groupCreateInputPreventionWindow
2776 *
2777 */
2778 void
groupCreateInputPreventionWindow(GroupSelection * group)2779 groupCreateInputPreventionWindow (GroupSelection *group)
2780 {
2781 if (!group->inputPrevention)
2782 {
2783 XSetWindowAttributes attrib;
2784 attrib.override_redirect = TRUE;
2785
2786 group->inputPrevention =
2787 XCreateWindow (group->screen->display->display,
2788 group->screen->root, -100, -100, 1, 1, 0,
2789 CopyFromParent, InputOnly,
2790 CopyFromParent, CWOverrideRedirect, &attrib);
2791 group->ipwMapped = FALSE;
2792 }
2793 }
2794
2795 /*
2796 * groupDestroyInputPreventionWindow
2797 *
2798 */
2799 void
groupDestroyInputPreventionWindow(GroupSelection * group)2800 groupDestroyInputPreventionWindow (GroupSelection *group)
2801 {
2802 if (group->inputPrevention)
2803 {
2804 XDestroyWindow (group->screen->display->display,
2805 group->inputPrevention);
2806
2807 group->inputPrevention = None;
2808 group->ipwMapped = TRUE;
2809 }
2810 }
2811