1 /*
2  *
3  * Compiz shift switcher plugin
4  *
5  * shift.c
6  *
7  * Copyright : (C) 2007 by Dennis Kasprzyk
8  * E-mail    : onestone@opencompositing.org
9  *
10  *
11  * Based on ring.c:
12  * Copyright : (C) 2007 by Danny Baumann
13  * E-mail    : maniac@opencompositing.org
14  *
15  * Based on scale.c and switcher.c:
16  * Copyright : (C) 2007 David Reveman
17  * E-mail    : davidr@novell.com
18  *
19  * Rounded corner drawing taken from wall.c:
20  * Copyright : (C) 2007 Robert Carr
21  * E-mail    : racarr@beryl-project.org
22  *
23  * This program is free software; you can redistribute it and/or
24  * modify it under the terms of the GNU General Public License
25  * as published by the Free Software Foundation; either version 2
26  * of the License, or (at your option) any later version.
27  *
28  * This program is distributed in the hope that it will be useful,
29  * but WITHOUT ANY WARRANTY; without even the implied warranty of
30  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
31  * GNU General Public License for more details.
32  *
33  */
34 
35 
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <math.h>
40 #include <sys/time.h>
41 
42 #include <X11/Xatom.h>
43 #include <X11/cursorfont.h>
44 
45 #include <compiz-core.h>
46 #include <compiz-text.h>
47 #include "shift_options.h"
48 
49 typedef enum {
50     ShiftStateNone = 0,
51     ShiftStateOut,
52     ShiftStateSwitching,
53     ShiftStateFinish,
54     ShiftStateIn
55 } ShiftState;
56 
57 typedef enum {
58     ShiftTypeNormal = 0,
59     ShiftTypeGroup,
60     ShiftTypeAll
61 } ShiftType;
62 
63 static int displayPrivateIndex;
64 
65 typedef struct _ShiftSlot {
66     int   x, y;            /* thumb center coordinates */
67     float z;
68     float scale;           /* size scale (fit to maximal thumb size */
69     float opacity;
70     float rotation;
71 
72     GLfloat tx;
73     GLfloat ty;
74 
75     Bool    primary;
76 
77 } ShiftSlot;
78 
79 typedef struct _ShiftDrawSlot {
80     CompWindow *w;
81     ShiftSlot  *slot;
82     float      distance;
83 } ShiftDrawSlot;
84 
85 typedef struct _ShiftDisplay {
86     int		    screenPrivateIndex;
87     HandleEventProc handleEvent;
88 
89     TextFunc *textFunc;
90 
91     KeyCode leftKey;
92     KeyCode rightKey;
93     KeyCode upKey;
94     KeyCode downKey;
95 } ShiftDisplay;
96 
97 typedef struct _ShiftScreen {
98     int windowPrivateIndex;
99 
100     PreparePaintScreenProc preparePaintScreen;
101     DonePaintScreenProc    donePaintScreen;
102     PaintScreenProc        paintScreen;
103     PaintOutputProc        paintOutput;
104     PaintWindowProc        paintWindow;
105     DamageWindowRectProc   damageWindowRect;
106 
107     int  grabIndex;
108 
109     ShiftState state;
110     ShiftType  type;
111 
112     Bool      moreAdjust;
113     Bool      moveAdjust;
114 
115     float   mvTarget;
116     float   mvAdjust;
117     GLfloat mvVelocity;
118     Bool    invert;
119 
120     Cursor cursor;
121 
122     /* only used for sorting */
123     CompWindow   **windows;
124     int          windowsSize;
125     int          nWindows;
126 
127     ShiftDrawSlot *drawSlots;
128     int           slotsSize;
129     int           nSlots;
130     ShiftDrawSlot *activeSlot;
131 
132     Window clientLeader;
133 
134     CompWindow *selectedWindow;
135 
136     /* text display support */
137     CompTextData *textData;
138 
139     CompMatch match;
140     CompMatch *currentMatch;
141 
142     CompOutput *output;
143     int        usedOutput;
144 
145     float anim;
146     float animVelocity;
147 
148     float reflectBrightness;
149     Bool  reflectActive;
150 
151     int	  buttonPressTime;
152     Bool  buttonPressed;
153     int   startX;
154     int   startY;
155     float startTarget;
156     float lastTitle;
157 
158     Bool  paintingAbove;
159 
160     Bool  canceled;
161 
162     int   maxThumbWidth;
163     int   maxThumbHeight;
164 } ShiftScreen;
165 
166 typedef struct _ShiftWindow {
167     ShiftSlot slots[2];
168 
169     float opacity;
170     float brightness;
171     float opacityVelocity;
172     float brightnessVelocity;
173 
174     Bool active;
175 } ShiftWindow;
176 
177 #define PI 3.1415926
178 #define ICON_SIZE 64
179 #define MAX_ICON_SIZE 256
180 
181 #define GET_SHIFT_DISPLAY(d)				      \
182     ((ShiftDisplay *) (d)->base.privates[displayPrivateIndex].ptr)
183 
184 #define SHIFT_DISPLAY(d)		     \
185     ShiftDisplay *sd = GET_SHIFT_DISPLAY (d)
186 
187 #define GET_SHIFT_SCREEN(s, sd)					  \
188     ((ShiftScreen *) (s)->base.privates[(sd)->screenPrivateIndex].ptr)
189 
190 #define SHIFT_SCREEN(s)							   \
191     ShiftScreen *ss = GET_SHIFT_SCREEN (s, GET_SHIFT_DISPLAY (s->display))
192 
193 #define GET_SHIFT_WINDOW(w, ss)					  \
194     ((ShiftWindow *) (w)->base.privates[(ss)->windowPrivateIndex].ptr)
195 
196 #define SHIFT_WINDOW(w)					       \
197     ShiftWindow *sw = GET_SHIFT_WINDOW  (w,		       \
198 	             GET_SHIFT_SCREEN  (w->screen,	       \
199 		     GET_SHIFT_DISPLAY (w->screen->display)))
200 
201 static void
shiftActivateEvent(CompScreen * s,Bool activating)202 shiftActivateEvent (CompScreen *s,
203 		    Bool       activating)
204 {
205     CompOption o[2];
206 
207     o[0].type = CompOptionTypeInt;
208     o[0].name = "root";
209     o[0].value.i = s->root;
210 
211     o[1].type = CompOptionTypeBool;
212     o[1].name = "active";
213     o[1].value.b = activating;
214 
215     (*s->display->handleCompizEvent) (s->display, "shift", "activate", o, 2);
216 }
217 
218 static Bool
isShiftWin(CompWindow * w)219 isShiftWin (CompWindow *w)
220 {
221     SHIFT_SCREEN (w->screen);
222 
223     if (w->destroyed)
224 	return FALSE;
225 
226     if (w->attrib.override_redirect)
227 	return FALSE;
228 
229     if (w->wmType & (CompWindowTypeDockMask | CompWindowTypeDesktopMask))
230 	return FALSE;
231 
232     if (!w->mapNum || w->attrib.map_state != IsViewable)
233     {
234 	if (shiftGetMinimized (w->screen))
235 	{
236 	    if (!w->minimized && !w->inShowDesktopMode && !w->shaded)
237 		return FALSE;
238 	}
239 	else
240     	    return FALSE;
241     }
242 
243     if (ss->type == ShiftTypeNormal)
244     {
245 	if (!w->mapNum || w->attrib.map_state != IsViewable)
246 	{
247 	    if (w->serverX + w->width  <= 0    ||
248 		w->serverY + w->height <= 0    ||
249 		w->serverX >= w->screen->width ||
250 		w->serverY >= w->screen->height)
251 		return FALSE;
252 	}
253 	else
254 	{
255 	    if (!(*w->screen->focusWindow) (w))
256 		return FALSE;
257 	}
258     }
259     else if (ss->type == ShiftTypeGroup &&
260 	     ss->clientLeader != w->clientLeader &&
261 	     ss->clientLeader != w->id)
262     {
263 	return FALSE;
264     }
265 
266     if (w->state & CompWindowStateSkipTaskbarMask)
267 	return FALSE;
268 
269     if (!matchEval (ss->currentMatch, w))
270 	return FALSE;
271 
272     return TRUE;
273 }
274 
275 static void
shiftFreeWindowTitle(CompScreen * s)276 shiftFreeWindowTitle (CompScreen *s)
277 {
278     SHIFT_SCREEN (s);
279     SHIFT_DISPLAY (s->display);
280 
281     if (!ss->textData)
282 	return;
283 
284     (sd->textFunc->finiTextData) (s, ss->textData);
285     ss->textData = NULL;
286 }
287 
288 static void
shiftRenderWindowTitle(CompScreen * s)289 shiftRenderWindowTitle (CompScreen *s)
290 {
291     CompTextAttrib tA;
292     int            ox1, ox2, oy1, oy2;
293 
294     SHIFT_SCREEN (s);
295     SHIFT_DISPLAY (s->display);
296 
297     shiftFreeWindowTitle (s);
298 
299     if (!sd->textFunc)
300 	return;
301 
302     if (!shiftGetWindowTitle (s))
303 	return;
304 
305     if (shiftGetMultioutputMode (s) == MultioutputModeOneBigSwitcher)
306     {
307 	ox1 = oy1 = 0;
308 	ox2 = s->width;
309 	oy2 = s->height;
310     }
311     else
312 	getCurrentOutputExtents (s, &ox1, &oy1, &ox2, &oy2);
313 
314     /* 75% of the output device as maximum width */
315     tA.maxWidth = (ox2 - ox1) * 3 / 4;
316     tA.maxHeight = 100;
317 
318     tA.family = "Sans";
319     tA.size = shiftGetTitleFontSize (s);
320     tA.color[0] = shiftGetTitleFontColorRed (s);
321     tA.color[1] = shiftGetTitleFontColorGreen (s);
322     tA.color[2] = shiftGetTitleFontColorBlue (s);
323     tA.color[3] = shiftGetTitleFontColorAlpha (s);
324 
325     tA.flags = CompTextFlagWithBackground | CompTextFlagEllipsized;
326     if (shiftGetTitleFontBold (s))
327 	tA.flags |= CompTextFlagStyleBold;
328 
329     tA.bgHMargin = 15;
330     tA.bgVMargin = 15;
331     tA.bgColor[0] = shiftGetTitleBackColorRed (s);
332     tA.bgColor[1] = shiftGetTitleBackColorGreen (s);
333     tA.bgColor[2] = shiftGetTitleBackColorBlue (s);
334     tA.bgColor[3] = shiftGetTitleBackColorAlpha (s);
335 
336     ss->textData = (sd->textFunc->renderWindowTitle) (s,
337 						      (ss->selectedWindow ?
338 						       ss->selectedWindow->id :
339 						       None),
340 						      ss->type == ShiftTypeAll,
341 						      &tA);
342 }
343 
344 static void
shiftDrawWindowTitle(CompScreen * s)345 shiftDrawWindowTitle (CompScreen *s)
346 {
347     float width, height, border = 10.0f;
348     int ox1, ox2, oy1, oy2;
349 
350     SHIFT_SCREEN (s);
351     SHIFT_DISPLAY (s->display);
352 
353     width = ss->textData->width;
354     height = ss->textData->height;
355 
356     if (shiftGetMultioutputMode (s) == MultioutputModeOneBigSwitcher)
357     {
358 	ox1 = oy1 = 0;
359 	ox2 = s->width;
360 	oy2 = s->height;
361     }
362     else
363     {
364         ox1 = s->outputDev[ss->usedOutput].region.extents.x1;
365         ox2 = s->outputDev[ss->usedOutput].region.extents.x2;
366         oy1 = s->outputDev[ss->usedOutput].region.extents.y1;
367         oy2 = s->outputDev[ss->usedOutput].region.extents.y2;
368     }
369 
370     float x = ox1 + ((ox2 - ox1) / 2) - ((int)ss->textData->width / 2);
371     float y;
372 
373     /* assign y (for the lower corner!) according to the setting */
374     switch (shiftGetTitleTextPlacement (s))
375     {
376     case TitleTextPlacementCenteredOnScreen:
377 	y = oy1 + ((oy2 - oy1) / 2) + (height / 2);
378 	break;
379     case TitleTextPlacementAbove:
380     case TitleTextPlacementBelow:
381 	{
382 	    XRectangle workArea;
383 	    getWorkareaForOutput (s, s->currentOutputDev, &workArea);
384 
385 	    if (shiftGetTitleTextPlacement (s) ==
386 		TitleTextPlacementAbove)
387 		y = oy1 + workArea.y + (2 * border) + height;
388 	    else
389 		y = oy1 + workArea.y + workArea.height - (2 * border);
390 	}
391 	break;
392     default:
393 	return;
394     }
395 
396     (sd->textFunc->drawText) (s, ss->textData, floor (x), floor (y), 1.0f);
397 }
398 
399 static Bool
shiftPaintWindow(CompWindow * w,const WindowPaintAttrib * attrib,const CompTransform * transform,Region region,unsigned int mask)400 shiftPaintWindow (CompWindow		 *w,
401        		 const WindowPaintAttrib *attrib,
402 		 const CompTransform	 *transform,
403 		 Region		         region,
404 		 unsigned int		 mask)
405 {
406     CompScreen *s = w->screen;
407     Bool       status;
408 
409     SHIFT_SCREEN (s);
410     SHIFT_WINDOW (w);
411 
412     if (ss->state != ShiftStateNone && !ss->paintingAbove)
413     {
414 	WindowPaintAttrib sAttrib = *attrib;
415 	Bool		  scaled = FALSE;
416 
417     	if (w->mapNum)
418 	{
419 	    if (!w->texture->pixmap && !w->bindFailed)
420 		bindWindow (w);
421 	}
422 
423 
424 	if (sw->active)
425 	    scaled = (ss->activeSlot != NULL);
426 
427 	if (sw->opacity > 0.01 && (ss->activeSlot == NULL))
428 	{
429 	    sAttrib.brightness = sAttrib.brightness * sw->brightness;
430 	    sAttrib.opacity = sAttrib.opacity * sw->opacity;
431 	}
432 	else
433 	    mask |= PAINT_WINDOW_NO_CORE_INSTANCE_MASK;
434 
435 	if (sw->active &&
436 	    (ss->output->id == ss->usedOutput || ss->output->id == ~0))
437 	    mask |= PAINT_WINDOW_NO_CORE_INSTANCE_MASK;
438 
439 
440 	UNWRAP (ss, s, paintWindow);
441 	status = (*s->paintWindow) (w, &sAttrib, transform, region, mask);
442 	WRAP (ss, s, paintWindow, shiftPaintWindow);
443 
444 	if (scaled && w->texture->pixmap)
445 	{
446 	    FragmentAttrib fragment;
447 	    CompTransform  wTransform = *transform;
448 	    ShiftSlot      *slot = ss->activeSlot->slot;
449 
450 	    float sx     = ss->anim * slot->tx;
451 	    float sy     = ss->anim * slot->ty;
452 	    float sz     = ss->anim * slot->z;
453 	    float srot   = (ss->anim * slot->rotation);
454 	    float anim   = MIN (1.0, MAX (0.0, ss->anim));
455 
456 	    float sscale;
457 	    float sopacity;
458 
459 
460 	    if (slot->primary)
461 		sscale = (ss->anim * slot->scale) + (1 - ss->anim);
462 	    else
463 		sscale = ss->anim * slot->scale;
464 
465 	    if (slot->primary && !ss->reflectActive)
466 		sopacity = (ss->anim * slot->opacity) + (1 - ss->anim);
467 	    else
468 		sopacity = anim * anim * slot->opacity;
469 
470 	    if (sopacity <= 0.05)
471 		return status;
472 
473 	    if (mask & PAINT_WINDOW_OCCLUSION_DETECTION_MASK)
474 		return FALSE;
475 
476 	    initFragmentAttrib (&fragment, attrib);
477 
478 	    fragment.opacity    = (float)fragment.opacity * sopacity;
479 	    fragment.brightness = (float)fragment.brightness *
480 				  ss->reflectBrightness;
481 
482 	    if (w->alpha || fragment.opacity != OPAQUE)
483 		mask |= PAINT_WINDOW_TRANSLUCENT_MASK;
484 
485 	    matrixTranslate (&wTransform, sx, sy, sz);
486 
487 	    matrixTranslate (&wTransform,
488 			     w->attrib.x + (w->attrib.width  * sscale / 2),
489 			     w->attrib.y + (w->attrib.height  * sscale / 2.0),
490 			     0.0f);
491 
492 	    matrixScale (&wTransform, ss->output->width, -ss->output->height,
493                 	 1.0f);
494 
495 	    matrixRotate (&wTransform, srot, 0.0, 1.0, 0.0);
496 
497 	    matrixScale (&wTransform, 1.0f  / ss->output->width,
498                 	 -1.0f / ss->output->height, 1.0f);
499 
500 	    matrixScale (&wTransform, sscale, sscale, 1.0f);
501 	    matrixTranslate (&wTransform, -w->attrib.x - (w->width / 2),
502 			     -w->attrib.y - (w->attrib.height / 2), 0.0f);
503 
504 	    glPushMatrix ();
505 	    glLoadMatrixf (wTransform.m);
506 
507 	    (*s->drawWindow) (w, &wTransform, &fragment, region,
508 			      mask | PAINT_WINDOW_TRANSFORMED_MASK);
509 
510 	    glPopMatrix ();
511 	}
512 
513 	if (scaled && ((shiftGetOverlayIcon (s) != OverlayIconNone) ||
514 	     !w->texture->pixmap))
515 	{
516 	    CompIcon *icon;
517 
518 	    icon = getWindowIcon (w, MAX_ICON_SIZE, MAX_ICON_SIZE);
519 	    if (!icon)
520 		icon = w->screen->defaultIcon;
521 
522 	    if (icon && (icon->texture.name || iconToTexture (w->screen, icon)))
523 	    {
524 		REGION iconReg;
525 		float  scale;
526 		float  x, y;
527 		int    width, height;
528 		int    scaledWinWidth, scaledWinHeight;
529 		ShiftOverlayIconEnum iconOverlay = shiftGetOverlayIcon (s);
530 		ShiftSlot      *slot = ss->activeSlot->slot;
531 
532 		float sx       = ss->anim * slot->tx;
533 		float sy       = ss->anim * slot->ty;
534 		float sz       = ss->anim * slot->z;
535 		float srot     = (ss->anim * slot->rotation);
536 		float sopacity = ss->anim * slot->opacity;
537 
538 		float sscale;
539 
540 		if (slot->primary)
541 		    sscale = (ss->anim * slot->scale) + (1 - ss->anim);
542 		else
543 		    sscale = ss->anim * ss->anim * slot->scale;
544 
545 		scaledWinWidth  = w->attrib.width  * sscale;
546 		scaledWinHeight = w->attrib.height * sscale;
547 
548 		if (!w->texture->pixmap)
549 		    iconOverlay = OverlayIconBig;
550 
551 	    	switch (iconOverlay)
552 		{
553 		case OverlayIconNone:
554 		case OverlayIconEmblem:
555 		    scale = MIN (((float) ICON_SIZE / icon->width),
556 				 ((float) ICON_SIZE / icon->height));
557 		    break;
558 		case OverlayIconBig:
559 		default:
560 		    if (w->texture->pixmap)
561 		    {
562 			/* only change opacity if not painting an
563 			   icon for a minimized window */
564 			sAttrib.opacity /= 3;
565 			scale = MIN (((float) scaledWinWidth / icon->width),
566 				     ((float) scaledWinHeight / icon->height));
567 		    }
568 		    else
569 		    {
570 			scale = MIN (((float) 128 / icon->width),
571 				     ((float) 128 / icon->height));
572 		    }
573 		    break;
574 		}
575 
576 		width  = icon->width  * scale;
577 		height = icon->height * scale;
578 
579 		switch (iconOverlay)
580 		{
581 		case OverlayIconNone:
582 		case OverlayIconEmblem:
583 		    x = scaledWinWidth - width;
584 		    y = scaledWinHeight - height;
585 		    break;
586 		case OverlayIconBig:
587 		default:
588 		    x = scaledWinWidth / 2 - width / 2;
589 		    if (w->texture->pixmap)
590 			y = scaledWinHeight / 2 - height / 2;
591 		    else
592 			y = scaledWinHeight - height;
593 		    break;
594 		}
595 
596 		mask |= PAINT_WINDOW_BLEND_MASK | PAINT_WINDOW_TRANSFORMED_MASK;
597 
598 		iconReg.rects    = &iconReg.extents;
599 		iconReg.numRects = 1;
600 
601 		iconReg.extents.x1 = 0;
602 		iconReg.extents.y1 = 0;
603 		iconReg.extents.x2 = icon->width;
604 		iconReg.extents.y2 = icon->height;
605 
606 		w->vCount = w->indexCount = 0;
607 		(*w->screen->addWindowGeometry) (w, &icon->texture.matrix, 1,
608 	    					 &iconReg, &infiniteRegion);
609 
610 		if (w->vCount)
611 		{
612 		    FragmentAttrib fragment;
613 		    CompTransform  wTransform = *transform;
614 
615 		    if (!w->texture->pixmap)
616 			sAttrib.opacity = w->paint.opacity;
617 
618 		    initFragmentAttrib (&fragment, &sAttrib);
619 
620 		    fragment.opacity = (float)fragment.opacity * sopacity;
621 		    fragment.brightness = (float)fragment.brightness *
622 					  ss->reflectBrightness;
623 
624 		    matrixTranslate (&wTransform, sx, sy, sz);
625 
626 		    matrixTranslate (&wTransform, w->attrib.x +
627 				     (w->width  * sscale / 2),
628 				     w->attrib.y +
629 				     (w->attrib.height  * sscale / 2.0), 0.0f);
630 
631 		    matrixScale (&wTransform, ss->output->width,
632                 		 -ss->output->height, 1.0f);
633 
634 		    matrixRotate (&wTransform, srot, 0.0, 1.0, 0.0);
635 
636 		    matrixScale (&wTransform, 1.0f  / ss->output->width,
637                 		 -1.0f / ss->output->height, 1.0f);
638 
639 		    matrixTranslate (&wTransform, x -
640 				     (w->attrib.width * sscale / 2), y -
641 				     (w->attrib.height * sscale / 2.0), 0.0f);
642 		    matrixScale (&wTransform, scale, scale, 1.0f);
643 
644 		    glPushMatrix ();
645 		    glLoadMatrixf (wTransform.m);
646 
647 		    (*w->screen->drawWindowTexture) (w,
648 						     &icon->texture, &fragment,
649 						     mask);
650 
651 		    glPopMatrix ();
652 		}
653 	    }
654 	}
655 
656     }
657     else
658     {
659 	WindowPaintAttrib sAttrib = *attrib;
660 
661 	if (ss->paintingAbove)
662 	{
663 	    sAttrib.opacity = sAttrib.opacity * (1.0 - ss->anim);
664 
665 	    if (ss->anim > 0.99)
666 		mask |= PAINT_WINDOW_NO_CORE_INSTANCE_MASK;
667 	}
668 
669 	UNWRAP (ss, s, paintWindow);
670 	status = (*s->paintWindow) (w, &sAttrib, transform, region, mask);
671 	WRAP (ss, s, paintWindow, shiftPaintWindow);
672     }
673 
674     return status;
675 }
676 
677 static void
switchActivateEvent(CompScreen * s,Bool activating)678 switchActivateEvent (CompScreen *s,
679 		     Bool	activating)
680 {
681     return;
682     CompOption o[2];
683 
684     o[0].type = CompOptionTypeInt;
685     o[0].name = "root";
686     o[0].value.i = s->root;
687 
688     o[1].type = CompOptionTypeBool;
689     o[1].name = "active";
690     o[1].value.b = activating;
691 
692     (*s->display->handleCompizEvent) (s->display, "switcher", "activate", o, 2);
693 }
694 
695 static int
compareWindows(const void * elem1,const void * elem2)696 compareWindows (const void *elem1,
697 		const void *elem2)
698 {
699     CompWindow *w1 = *((CompWindow **) elem1);
700     CompWindow *w2 = *((CompWindow **) elem2);
701     CompWindow *w  = w1;
702 
703     if (w1 == w2)
704 	return 0;
705 
706     if (!w1->shaded && w1->attrib.map_state != IsViewable &&
707         (w2->shaded || w2->attrib.map_state == IsViewable))
708     {
709 	return 1;
710     }
711 
712     if (!w2->shaded && w2->attrib.map_state != IsViewable &&
713         (w1->shaded || w1->attrib.map_state == IsViewable))
714     {
715 	return -1;
716     }
717 
718     while (w)
719     {
720 	if (w == w2)
721 	    return 1;
722 	w = w->next;
723     }
724     return -1;
725 }
726 
727 static int
compareShiftWindowDistance(const void * elem1,const void * elem2)728 compareShiftWindowDistance (const void *elem1,
729 			    const void *elem2)
730 {
731     float a1   = ((ShiftDrawSlot *) elem1)->distance;
732     float a2   = ((ShiftDrawSlot *) elem2)->distance;
733     float ab   = fabs (a1 - a2);
734 
735     if (ab > 0.3 && a1 > a2)
736 	return -1;
737     else if (ab > 0.3 && a1 < a2)
738 	return 1;
739     else
740 	return compareWindows (&((ShiftDrawSlot *) elem2)->w,
741 			       &((ShiftDrawSlot *) elem1)->w);
742 }
743 
744 static Bool
layoutThumbsCover(CompScreen * s)745 layoutThumbsCover (CompScreen *s)
746 {
747     SHIFT_SCREEN (s);
748     CompWindow *w;
749     int index;
750     int ww, wh;
751     float xScale, yScale;
752     float distance;
753     int i;
754 
755     int ox1, ox2, oy1, oy2;
756 
757     if (shiftGetMultioutputMode (s) == MultioutputModeOneBigSwitcher)
758     {
759 	ox1 = oy1 = 0;
760 	ox2 = s->width;
761 	oy2 = s->height;
762     }
763     else
764     {
765         ox1 = s->outputDev[ss->usedOutput].region.extents.x1;
766         ox2 = s->outputDev[ss->usedOutput].region.extents.x2;
767         oy1 = s->outputDev[ss->usedOutput].region.extents.y1;
768         oy2 = s->outputDev[ss->usedOutput].region.extents.y2;
769     }
770 
771     /* the center of the ellipse is in the middle
772        of the used output device */
773     int centerX = ox1 + (ox2 - ox1) / 2;
774     int centerY = oy1 + (oy2 - oy1) / 2;
775 
776     ss->maxThumbWidth  = (ox2 - ox1) * shiftGetSize(s) / 100;
777     ss->maxThumbHeight = (oy2 - oy1) * shiftGetSize(s) / 100;
778 
779     for (index = 0; index < ss->nWindows; index++)
780     {
781 	w = ss->windows[index];
782 	SHIFT_WINDOW (w);
783 
784 	ww = w->attrib.width  + w->input.left + w->input.right;
785 	wh = w->attrib.height + w->input.top  + w->input.bottom;
786 
787 	if (ww > ss->maxThumbWidth)
788 	    xScale = (float)(ss->maxThumbWidth) / (float)ww;
789 	else
790 	    xScale = 1.0f;
791 
792 	if (wh > ss->maxThumbHeight)
793 	    yScale = (float)(ss->maxThumbHeight) / (float)wh;
794 	else
795 	    yScale = 1.0f;
796 
797 
798 	float val1 = floor((float)ss->nWindows / 2.0);
799 
800 	float pos;
801 	float space = ss->maxThumbWidth / 2;
802 	space *= cos (sin (PI / 4) * PI / 3);
803 	space *= 2;
804 	//space += (space / sin (PI / 4)) - space;
805 
806 	for (i = 0; i < 2; i++)
807 	{
808 
809 
810 		if (ss->invert ^ (i == 0))
811 		{
812 		    distance = ss->mvTarget - index;
813 		    distance += shiftGetCoverOffset (s);
814 		}
815 		else
816 		{
817 		    distance = ss->mvTarget - index + ss->nWindows;
818 		    distance += shiftGetCoverOffset (s);
819 		    if (distance > ss->nWindows)
820 			distance -= ss->nWindows * 2;
821 		}
822 
823 
824 		pos = MIN (1.0, MAX (-1.0, distance));
825 
826 		sw->slots[i].opacity = 1.0 - MIN (1.0,
827 				       MAX (0.0, fabs(distance) - val1));
828 		sw->slots[i].scale   = MIN (xScale, yScale);
829 
830 		sw->slots[i].y = centerY + (ss->maxThumbHeight / 2.0) -
831 				 (((w->attrib.height / 2.0) + w->input.bottom) *
832 				 sw->slots[i].scale);
833 
834 		if (fabs(distance) < 1.0)
835 		{
836 		    sw->slots[i].x  = centerX + (sin(pos * PI * 0.5) * space);
837 		    sw->slots[i].z  = fabs (distance);
838 		    sw->slots[i].z *= -(ss->maxThumbWidth / (2.0 * (ox2 - ox1)));
839 
840 		    sw->slots[i].rotation = sin(pos * PI * 0.5) * -60;
841 		}
842 		else
843 		{
844 		    float rad = (space / (ox2 - ox1)) / sin(PI / 6.0);
845 
846 		    float ang = (PI / MAX(72.0, ss->nWindows * 2)) *
847 				(distance - pos) + (pos * (PI / 6.0));
848 
849 		    sw->slots[i].x  = centerX;
850 		    sw->slots[i].x += sin(ang) * rad * (ox2 - ox1);
851 
852 		    sw->slots[i].rotation  = 90;
853 		    sw->slots[i].rotation -= fabs(ang) * 180.0 / PI;
854 		    sw->slots[i].rotation *= -pos;
855 
856 		    sw->slots[i].z  = -(ss->maxThumbWidth / (2.0 * (ox2 - ox1)));
857 		    sw->slots[i].z += -(cos(PI / 6.0) * rad);
858 		    sw->slots[i].z += (cos(ang) * rad);
859 		}
860 
861 		ss->drawSlots[index * 2 + i].w     = w;
862 		ss->drawSlots[index * 2 + i].slot  = &sw->slots[i];
863 		ss->drawSlots[index * 2 + i].distance = fabs(distance);
864 
865 	}
866 
867 	if (ss->drawSlots[index * 2].distance >
868 	    ss->drawSlots[index * 2 + 1].distance)
869 	{
870 	    ss->drawSlots[index * 2].slot->primary     = FALSE;
871 	    ss->drawSlots[index * 2 + 1].slot->primary = TRUE;
872 	}
873 	else
874 	{
875 	    ss->drawSlots[index * 2].slot->primary     = TRUE;
876 	    ss->drawSlots[index * 2 + 1].slot->primary = FALSE;
877 	}
878 
879     }
880 
881     ss->nSlots = ss->nWindows * 2;
882 
883     qsort (ss->drawSlots, ss->nSlots, sizeof (ShiftDrawSlot),
884 	   compareShiftWindowDistance);
885 
886     return TRUE;
887 }
888 
889 static Bool
layoutThumbsFlip(CompScreen * s)890 layoutThumbsFlip (CompScreen *s)
891 {
892     SHIFT_SCREEN (s);
893     CompWindow *w;
894     int index;
895     int ww, wh;
896     float xScale, yScale;
897     float distance;
898     int i;
899     float angle;
900     int slotNum;
901 
902     int ox1, ox2, oy1, oy2;
903 
904     if (shiftGetMultioutputMode (s) == MultioutputModeOneBigSwitcher)
905     {
906 	ox1 = oy1 = 0;
907 	ox2 = s->width;
908 	oy2 = s->height;
909     }
910     else
911     {
912         ox1 = s->outputDev[ss->usedOutput].region.extents.x1;
913         ox2 = s->outputDev[ss->usedOutput].region.extents.x2;
914         oy1 = s->outputDev[ss->usedOutput].region.extents.y1;
915         oy2 = s->outputDev[ss->usedOutput].region.extents.y2;
916     }
917 
918     /* the center of the ellipse is in the middle
919        of the used output device */
920     int centerX = ox1 + (ox2 - ox1) / 2;
921     int centerY = oy1 + (oy2 - oy1) / 2;
922 
923     ss->maxThumbWidth  = (ox2 - ox1) * shiftGetSize(s) / 100;
924     ss->maxThumbHeight = (oy2 - oy1) * shiftGetSize(s) / 100;
925 
926     slotNum = 0;
927 
928     for (index = 0; index < ss->nWindows; index++)
929     {
930 	w = ss->windows[index];
931 	SHIFT_WINDOW (w);
932 
933 	ww = w->attrib.width  + w->input.left + w->input.right;
934 	wh = w->attrib.height + w->input.top  + w->input.bottom;
935 
936 	if (ww > ss->maxThumbWidth)
937 	    xScale = (float)(ss->maxThumbWidth) / (float)ww;
938 	else
939 	    xScale = 1.0f;
940 
941 	if (wh > ss->maxThumbHeight)
942 	    yScale = (float)(ss->maxThumbHeight) / (float)wh;
943 	else
944 	    yScale = 1.0f;
945 
946 	angle = shiftGetFlipRotation (s) * PI / 180.0;
947 
948 	for (i = 0; i < 2; i++)
949 	{
950 
951 		if (ss->invert ^ (i == 0))
952 		    distance = ss->mvTarget - index;
953 		else
954 		{
955 		    distance = ss->mvTarget - index + ss->nWindows;
956 		    if (distance > 1.0)
957 			distance -= ss->nWindows * 2;
958 		}
959 
960 		if (distance > 0.0)
961 		    sw->slots[i].opacity = MAX (0.0, 1.0 - (distance * 1.0));
962 		else
963 		{
964 		    if (distance < -(ss->nWindows - 1))
965 		    	sw->slots[i].opacity = MAX (0.0, ss->nWindows +
966 						    distance);
967 		    else
968 			sw->slots[i].opacity = 1.0;
969 		}
970 
971 		if (distance > 0.0 && w != ss->selectedWindow)
972 		    sw->slots[i].primary = FALSE;
973 		else
974 		    sw->slots[i].primary = TRUE;
975 
976 
977 		sw->slots[i].scale   = MIN (xScale, yScale);
978 
979 		sw->slots[i].y = centerY + (ss->maxThumbHeight / 2.0) -
980 				 (((w->attrib.height / 2.0) + w->input.bottom) *
981 				 sw->slots[i].scale);
982 
983 		sw->slots[i].x  = sin(angle) * distance * (ss->maxThumbWidth / 2);
984 		if (distance > 0 && FALSE)
985 		    sw->slots[i].x *= 1.5;
986 		sw->slots[i].x += centerX;
987 
988 		sw->slots[i].z  = cos(angle) * distance;
989 		if (distance > 0)
990 		    sw->slots[i].z *= 1.5;
991 		sw->slots[i].z *= (ss->maxThumbWidth / (2.0 * (ox2 - ox1)));
992 
993 		sw->slots[i].rotation = shiftGetFlipRotation (s);
994 
995 		if (sw->slots[i].opacity > 0.0)
996 		{
997 		    ss->drawSlots[slotNum].w     = w;
998 		    ss->drawSlots[slotNum].slot  = &sw->slots[i];
999 		    ss->drawSlots[slotNum].distance = -distance;
1000 		    slotNum++;
1001 		}
1002 	}
1003     }
1004 
1005     ss->nSlots = slotNum;
1006 
1007     qsort (ss->drawSlots, ss->nSlots, sizeof (ShiftDrawSlot),
1008 	   compareShiftWindowDistance);
1009 
1010     return TRUE;
1011 }
1012 
1013 
1014 static Bool
layoutThumbs(CompScreen * s)1015 layoutThumbs (CompScreen *s)
1016 {
1017     Bool result = FALSE;
1018 
1019     SHIFT_SCREEN (s);
1020 
1021     if (ss->state == ShiftStateNone)
1022 	return FALSE;
1023 
1024     switch (shiftGetMode (s))
1025     {
1026     case ModeCover:
1027 	result = layoutThumbsCover (s);
1028 	break;
1029     case ModeFlip:
1030     	result = layoutThumbsFlip (s);
1031     	break;
1032     }
1033 
1034     if (ss->state == ShiftStateIn)
1035     	return FALSE;
1036 
1037     return result;
1038 }
1039 
1040 
1041 static void
shiftAddWindowToList(CompScreen * s,CompWindow * w)1042 shiftAddWindowToList (CompScreen *s,
1043 		     CompWindow *w)
1044 {
1045     SHIFT_SCREEN (s);
1046 
1047     if (ss->windowsSize <= ss->nWindows)
1048     {
1049 	ss->windows = realloc (ss->windows,
1050 			       sizeof (CompWindow *) * (ss->nWindows + 32));
1051 	if (!ss->windows)
1052 	    return;
1053 
1054 	ss->windowsSize = ss->nWindows + 32;
1055     }
1056 
1057     if (ss->slotsSize <= ss->nWindows * 2)
1058     {
1059 	ss->drawSlots = realloc (ss->drawSlots,
1060 				 sizeof (ShiftDrawSlot) *
1061 				 ((ss->nWindows * 2) + 64));
1062 
1063 	if (!ss->drawSlots)
1064 	    return;
1065 
1066 	ss->slotsSize = (ss->nWindows * 2) + 64;
1067     }
1068 
1069     ss->windows[ss->nWindows++] = w;
1070 }
1071 
1072 static Bool
shiftUpdateWindowList(CompScreen * s)1073 shiftUpdateWindowList (CompScreen *s)
1074 {
1075     SHIFT_SCREEN (s);
1076 
1077     int        i, idx;
1078     CompWindow **wins;
1079 
1080     qsort (ss->windows, ss->nWindows, sizeof (CompWindow *), compareWindows);
1081 
1082     ss->mvTarget = 0;
1083     ss->mvAdjust = 0;
1084     ss->mvVelocity = 0;
1085     for (i = 0; i < ss->nWindows; i++)
1086     {
1087 	if (ss->windows[i] == ss->selectedWindow)
1088 	    break;
1089 
1090 	ss->mvTarget++;
1091     }
1092     if (ss->mvTarget == ss->nWindows)
1093 	ss->mvTarget = 0;
1094 
1095     /* create spetial window order to create a good animation
1096        A,B,C,D,E --> A,B,D,E,C to get B,D,E,C,(A),B,D,E,C as initial state */
1097     if (shiftGetMode (s) == ModeCover)
1098     {
1099 	wins = malloc(ss->nWindows * sizeof (CompWindow *));
1100 	if (!wins)
1101 	    return FALSE;
1102 
1103 	memcpy(wins, ss->windows, ss->nWindows * sizeof (CompWindow *));
1104 	for (i = 0; i < ss->nWindows; i++)
1105 	{
1106 	    idx = ceil (i * 0.5);
1107 	    idx *= (i & 1) ? 1 : -1;
1108 	    if (idx < 0)
1109 		idx += ss->nWindows;
1110 	    ss->windows[idx] = wins[i];
1111 	}
1112 	free (wins);
1113     }
1114 
1115     return layoutThumbs (s);
1116 }
1117 
1118 static Bool
shiftCreateWindowList(CompScreen * s)1119 shiftCreateWindowList (CompScreen *s)
1120 {
1121     CompWindow *w;
1122     SHIFT_SCREEN (s);
1123 
1124     ss->nWindows = 0;
1125 
1126     for (w = s->windows; w; w = w->next)
1127     {
1128 	if (isShiftWin (w))
1129 	{
1130 	    SHIFT_WINDOW (w);
1131 
1132 	    shiftAddWindowToList (s, w);
1133 	    sw->active = TRUE;
1134 	}
1135     }
1136 
1137     return shiftUpdateWindowList (s);
1138 }
1139 
1140 static void
switchToWindow(CompScreen * s,Bool toNext)1141 switchToWindow (CompScreen *s,
1142 		Bool	   toNext)
1143 {
1144     CompWindow *w;
1145     int	       cur;
1146 
1147     SHIFT_SCREEN (s);
1148 
1149     if (!ss->grabIndex)
1150 	return;
1151 
1152     for (cur = 0; cur < ss->nWindows; cur++)
1153     {
1154 	if (ss->windows[cur] == ss->selectedWindow)
1155 	    break;
1156     }
1157 
1158     if (cur == ss->nWindows)
1159 	return;
1160 
1161     if (toNext)
1162 	w = ss->windows[(cur + 1) % ss->nWindows];
1163     else
1164 	w = ss->windows[(cur + ss->nWindows - 1) % ss->nWindows];
1165 
1166     if (w)
1167     {
1168 	CompWindow *old = ss->selectedWindow;
1169 	ss->selectedWindow = w;
1170 
1171 	if (old != w)
1172 	{
1173 	    if (toNext)
1174 		ss->mvAdjust += 1;
1175 	    else
1176 		ss->mvAdjust -= 1;
1177 
1178 	    ss->moveAdjust = TRUE;
1179 	    damageScreen (s);
1180 	    shiftRenderWindowTitle (s);
1181 	}
1182     }
1183 }
1184 
1185 static int
shiftCountWindows(CompScreen * s)1186 shiftCountWindows (CompScreen *s)
1187 {
1188     CompWindow *w;
1189     int	       count = 0;
1190 
1191     for (w = s->windows; w; w = w->next)
1192     {
1193 	if (isShiftWin (w))
1194 	    count++;
1195     }
1196 
1197     return count;
1198 }
1199 
adjustShiftMovement(CompScreen * s,float chunk)1200 static int adjustShiftMovement (CompScreen *s, float chunk)
1201 {
1202     float dx, adjust, amount;
1203     float change;
1204 
1205     SHIFT_SCREEN(s);
1206 
1207     dx = ss->mvAdjust;
1208 
1209     adjust = dx * 0.15f;
1210     amount = fabs(dx) * 1.5f;
1211     if (amount < 0.2f)
1212 	amount = 0.2f;
1213     else if (amount > 2.0f)
1214 	amount = 2.0f;
1215 
1216     ss->mvVelocity = (amount * ss->mvVelocity + adjust) / (amount + 1.0f);
1217 
1218     if (fabs (dx) < 0.002f && fabs (ss->mvVelocity) < 0.004f)
1219     {
1220 	ss->mvVelocity = 0.0f;
1221 	ss->mvTarget = ss->mvTarget + ss->mvAdjust;
1222 	ss->mvAdjust = 0;
1223 	layoutThumbs (s);
1224 	return FALSE;
1225     }
1226 
1227     change = ss->mvVelocity * chunk;
1228     if (!change)
1229     {
1230 	if (ss->mvVelocity)
1231 	    change = (ss->mvAdjust > 0) ? 0.01 : -0.01;
1232     }
1233 
1234     ss->mvAdjust -= change;
1235     ss->mvTarget += change;
1236 
1237     while (ss->mvTarget >= ss->nWindows)
1238     {
1239 	ss->mvTarget -= ss->nWindows;
1240 	ss->invert = !ss->invert;
1241     }
1242 
1243     while (ss->mvTarget < 0)
1244     {
1245 	ss->mvTarget += ss->nWindows;
1246 	ss->invert = !ss->invert;
1247     }
1248 
1249     if (!layoutThumbs (s))
1250 	return FALSE;
1251 
1252     return TRUE;
1253 }
1254 
1255 static Bool
adjustShiftWindowAttribs(CompWindow * w,float chunk)1256 adjustShiftWindowAttribs (CompWindow *w, float chunk)
1257 {
1258     float dp, db, adjust, amount;
1259     float opacity, brightness;
1260 
1261     SHIFT_WINDOW (w);
1262     SHIFT_SCREEN (w->screen);
1263 
1264     if ((sw->active && ss->state != ShiftStateIn &&
1265 	ss->state != ShiftStateNone) ||
1266 	(shiftGetHideAll(w->screen) && !(w->type & CompWindowTypeDesktopMask) &&
1267 	(ss->state == ShiftStateOut || ss->state == ShiftStateSwitching ||
1268 	 ss->state == ShiftStateFinish)))
1269 	opacity = 0.0;
1270     else
1271 	opacity = 1.0;
1272 
1273     if (ss->state == ShiftStateIn || ss->state == ShiftStateNone)
1274 	brightness = 1.0;
1275     else
1276 	brightness = shiftGetBackgroundIntensity (w->screen);
1277 
1278     dp = opacity - sw->opacity;
1279     adjust = dp * 0.1f;
1280     amount = fabs (dp) * 7.0f;
1281     if (amount < 0.01f)
1282 	amount = 0.01f;
1283     else if (amount > 0.15f)
1284 	amount = 0.15f;
1285 
1286     sw->opacityVelocity = (amount * sw->opacityVelocity + adjust) /
1287 	(amount + 1.0f);
1288 
1289     db = brightness - sw->brightness;
1290     adjust = db * 0.1f;
1291     amount = fabs (db) * 7.0f;
1292     if (amount < 0.01f)
1293 	amount = 0.01f;
1294     else if (amount > 0.15f)
1295 	amount = 0.15f;
1296 
1297     sw->brightnessVelocity = (amount * sw->brightnessVelocity + adjust) /
1298 	(amount + 1.0f);
1299 
1300 
1301     if (fabs (dp) < 0.01f && fabs (sw->opacityVelocity) < 0.02f &&
1302 	fabs (db) < 0.01f && fabs (sw->brightnessVelocity) < 0.02f)
1303     {
1304 
1305 	sw->brightness = brightness;
1306 	sw->opacity = opacity;
1307 	return FALSE;
1308     }
1309 
1310     sw->brightness += sw->brightnessVelocity * chunk;
1311     sw->opacity += sw->opacityVelocity * chunk;
1312 
1313     return TRUE;
1314 }
1315 
1316 static Bool
adjustShiftAnimationAttribs(CompScreen * s,float chunk)1317 adjustShiftAnimationAttribs (CompScreen *s, float chunk)
1318 {
1319     float dr, adjust, amount;
1320     float anim;
1321 
1322     SHIFT_SCREEN (s);
1323 
1324     if (ss->state != ShiftStateIn && ss->state != ShiftStateNone)
1325 	anim = 1.0;
1326     else
1327 	anim = 0.0;
1328 
1329     dr = anim - ss->anim;
1330     adjust = dr * 0.1f;
1331     amount = fabs (dr) * 7.0f;
1332     if (amount < 0.002f)
1333 	amount = 0.002f;
1334     else if (amount > 0.15f)
1335 	amount = 0.15f;
1336 
1337     ss->animVelocity = (amount * ss->animVelocity + adjust) /
1338 	(amount + 1.0f);
1339 
1340     if (fabs (dr) < 0.002f && fabs (ss->animVelocity) < 0.004f)
1341     {
1342 
1343 	ss->anim = anim;
1344 	return FALSE;
1345     }
1346 
1347     ss->anim += ss->animVelocity * chunk;
1348     return TRUE;
1349 }
1350 
1351 static Bool
shiftPaintOutput(CompScreen * s,const ScreenPaintAttrib * sAttrib,const CompTransform * transform,Region region,CompOutput * output,unsigned int mask)1352 shiftPaintOutput (CompScreen		  *s,
1353 		 const ScreenPaintAttrib *sAttrib,
1354 		 const CompTransform	  *transform,
1355 		 Region		          region,
1356 		 CompOutput		  *output,
1357 		 unsigned int		  mask)
1358 {
1359     Bool status;
1360 
1361     SHIFT_SCREEN (s);
1362 
1363     if (ss->state != ShiftStateNone)
1364 	mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS_MASK;
1365 
1366     ss->paintingAbove = FALSE;
1367 
1368     ss->output = output;
1369 
1370     UNWRAP (ss, s, paintOutput);
1371     status = (*s->paintOutput) (s, sAttrib, transform, region, output, mask);
1372     WRAP (ss, s, paintOutput, shiftPaintOutput);
1373 
1374     if (ss->state != ShiftStateNone &&
1375 	(output->id == ss->usedOutput || output->id == ~0))
1376     {
1377 	CompWindow    *w;
1378 	CompTransform sTransform = *transform;
1379 	int           i;
1380 	int           oy1 = s->outputDev[ss->usedOutput].region.extents.y1;
1381 	int           oy2 = s->outputDev[ss->usedOutput].region.extents.y2;
1382 	int           maxThumbHeight = (oy2 - oy1) * shiftGetSize(s) / 100;
1383 	int           oldFilter = s->display->textureFilter;
1384 
1385 	if (shiftGetMultioutputMode (s) == MultioutputModeOneBigSwitcher)
1386 	{
1387 	    oy1 = 0;
1388 	    oy2 = s->height;
1389 	}
1390 
1391 	transformToScreenSpace (s, output, -DEFAULT_Z_CAMERA, &sTransform);
1392 
1393 	GLdouble clip[4] = { 0.0, -1.0, 0.0, 0.0};
1394 
1395 	clip[3] = ((oy1 + (oy2 - oy1) / 2)) + (maxThumbHeight / 2.0);
1396 
1397 	if (shiftGetReflection (s))
1398 	{
1399 	    CompTransform  rTransform = sTransform;
1400 	    unsigned short color[4];
1401 	    int            cull, cullInv;
1402 	    glGetIntegerv (GL_CULL_FACE_MODE, &cull);
1403 	    cullInv = (cull == GL_BACK)? GL_FRONT : GL_BACK;
1404 
1405 	    matrixTranslate (&rTransform, 0.0, oy1 + oy2 + maxThumbHeight,
1406 			     0.0);
1407 	    matrixScale (&rTransform, 1.0, -1.0, 1.0);
1408 
1409 	    glPushMatrix ();
1410 	    glLoadMatrixf (rTransform.m);
1411 
1412 	    glCullFace (cullInv);
1413 
1414 	    if (shiftGetMipmaps (s))
1415 		s->display->textureFilter = GL_LINEAR_MIPMAP_LINEAR;
1416 
1417 
1418 	    if (ss->anim == 1.0)
1419 	    {
1420 		glClipPlane (GL_CLIP_PLANE0, clip);
1421 		glEnable (GL_CLIP_PLANE0);
1422 	    }
1423 
1424 	    ss->reflectActive = TRUE;
1425 	    ss->reflectBrightness = shiftGetIntensity(s);
1426 	    for (i = 0; i < ss->nSlots; i++)
1427 	    {
1428 		w = ss->drawSlots[i].w;
1429 
1430 		ss->activeSlot = &ss->drawSlots[i];
1431 		{
1432 		    (*s->paintWindow) (w, &w->paint, &rTransform,
1433 				       &infiniteRegion, 0);
1434 		}
1435 	    }
1436 
1437 	    glDisable (GL_CLIP_PLANE0);
1438 	    glCullFace (cull);
1439 
1440 	    glLoadIdentity();
1441 	    glTranslatef (0.0, 0.0, -DEFAULT_Z_CAMERA);
1442 
1443 	    glEnable(GL_BLEND);
1444 	    glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1445 
1446 	    glBegin (GL_QUADS);
1447 	    glColor4f (0.0, 0.0, 0.0, 0.0);
1448 	    glVertex2f (0.5, 0.0);
1449 	    glVertex2f (-0.5, 0.0);
1450 	    glColor4f (0.0, 0.0, 0.0,
1451 		       MIN (1.0, 1.0 - shiftGetIntensity (s)) * 2.0 *
1452 		       ss->anim);
1453 	    glVertex2f (-0.5, -0.5);
1454 	    glVertex2f (0.5, -0.5);
1455 	    glEnd();
1456 
1457 	    if (shiftGetGroundSize (s) > 0.0)
1458 	    {
1459 		glBegin (GL_QUADS);
1460 		color[0] = shiftGetGroundColor1 (s)[0];
1461 		color[1] = shiftGetGroundColor1 (s)[1];
1462 		color[2] = shiftGetGroundColor1 (s)[2];
1463 		color[3] = (float)shiftGetGroundColor1 (s)[3] * ss->anim;
1464 		glColor4usv (color);
1465 		glVertex2f (-0.5, -0.5);
1466 		glVertex2f (0.5, -0.5);
1467 		color[0] = shiftGetGroundColor2 (s)[0];
1468 		color[1] = shiftGetGroundColor2 (s)[1];
1469 		color[2] = shiftGetGroundColor2 (s)[2];
1470 		color[3] = (float)shiftGetGroundColor2 (s)[3] * ss->anim;
1471 		glColor4usv (color);
1472 		glVertex2f (0.5, -0.5 + shiftGetGroundSize (s));
1473 		glVertex2f (-0.5, -0.5 + shiftGetGroundSize (s));
1474 		glEnd();
1475 	    }
1476 
1477 	    glBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
1478 	    glDisable(GL_BLEND);
1479 	    glColor4f (1.0, 1.0, 1.0, 1.0);
1480 	    glPopMatrix ();
1481 	}
1482 
1483 	glPushMatrix ();
1484 	glLoadMatrixf (sTransform.m);
1485 
1486 	if (shiftGetReflection (s) && ss->anim == 1.0)
1487 	{
1488 	    glClipPlane (GL_CLIP_PLANE0, clip);
1489 	    glEnable (GL_CLIP_PLANE0);
1490 	}
1491 
1492 	ss->reflectBrightness = 1.0;
1493 	ss->reflectActive     = FALSE;
1494 
1495 	for (i = 0; i < ss->nSlots; i++)
1496 	{
1497 	    w = ss->drawSlots[i].w;
1498 
1499 	    ss->activeSlot = &ss->drawSlots[i];
1500 	    {
1501 		(*s->paintWindow) (w, &w->paint, &sTransform,
1502 				   &infiniteRegion, 0);
1503 	    }
1504 	}
1505 
1506 	glDisable (GL_CLIP_PLANE0);
1507 
1508 	ss->activeSlot = NULL;
1509 
1510 	s->display->textureFilter = oldFilter;
1511 
1512 	if (ss->textData && (ss->state != ShiftStateIn))
1513 	    shiftDrawWindowTitle (s);
1514 
1515 	if (ss->state == ShiftStateIn || ss->state == ShiftStateOut)
1516 	{
1517 	    Bool found;
1518 	    ss->paintingAbove = TRUE;
1519 
1520 	    for (w = ss->selectedWindow; w; w = w->next)
1521 	    {
1522 		if (w->destroyed)
1523 		    continue;
1524 
1525 		if (!w->shaded)
1526 		{
1527 		    if (w->attrib.map_state != IsViewable || !w->damaged)
1528 			continue;
1529 		}
1530 		found = FALSE;
1531 		for (i = 0; i < ss->nWindows; i++)
1532 		    if (ss->windows[i] == w)
1533 			found = TRUE;
1534 		if (found)
1535 		    continue;
1536 		(*s->paintWindow) (w, &w->paint, &sTransform,
1537 				   &infiniteRegion, 0);
1538 	    }
1539 
1540 	    ss->paintingAbove = FALSE;
1541 	}
1542 
1543 	glPopMatrix ();
1544     }
1545 
1546     return status;
1547 }
1548 
1549 static void
shiftPaintScreen(CompScreen * s,CompOutput * outputs,int numOutputs,unsigned int mask)1550 shiftPaintScreen (CompScreen   *s,
1551 		  CompOutput   *outputs,
1552 		  int          numOutputs,
1553 		  unsigned int mask)
1554 {
1555     SHIFT_SCREEN (s);
1556 
1557     if (ss->state != ShiftStateNone && numOutputs > 0 &&
1558         shiftGetMultioutputMode (s) != MultioutputModeDisabled)
1559     {
1560 	outputs = &s->fullscreenOutput;
1561 	numOutputs = 1;
1562     }
1563 
1564     UNWRAP (ss, s, paintScreen);
1565     (*s->paintScreen) (s, outputs, numOutputs, mask);
1566     WRAP (ss, s, paintScreen, shiftPaintScreen);
1567 }
1568 
1569 static void
shiftPreparePaintScreen(CompScreen * s,int msSinceLastPaint)1570 shiftPreparePaintScreen (CompScreen *s,
1571 			int	    msSinceLastPaint)
1572 {
1573     SHIFT_SCREEN (s);
1574 
1575     if (ss->state != ShiftStateNone &&
1576 	(ss->moreAdjust || ss->moveAdjust))
1577     {
1578 	CompWindow *w;
1579 	int        steps;
1580 	float      amount, chunk;
1581 	int        i;
1582 
1583 	amount = msSinceLastPaint * 0.05f * shiftGetShiftSpeed (s);
1584 	steps  = amount / (0.5f * shiftGetTimestep (s));
1585 
1586 	if (!steps)
1587 	    steps = 1;
1588 	chunk  = amount / (float) steps;
1589 
1590 
1591 	while (steps--)
1592 	{
1593 	    ss->moveAdjust = adjustShiftMovement (s, chunk);
1594 	    if (!ss->moveAdjust)
1595 		break;
1596 	}
1597 
1598 	amount = msSinceLastPaint * 0.05f * shiftGetSpeed (s);
1599 	steps  = amount / (0.5f * shiftGetTimestep (s));
1600 
1601 	if (!steps)
1602 	    steps = 1;
1603 	chunk  = amount / (float) steps;
1604 
1605 	while (steps--)
1606 	{
1607 	    ss->moreAdjust = adjustShiftAnimationAttribs (s, chunk);
1608 
1609 	    for (w = s->windows; w; w = w->next)
1610 	    {
1611 		SHIFT_WINDOW (w);
1612 
1613 		ss->moreAdjust |= adjustShiftWindowAttribs (w, chunk);
1614 		for (i = 0; i < 2; i++)
1615 		{
1616 		    ShiftSlot *slot = &sw->slots[i];
1617 		    slot->tx = slot->x - w->attrib.x -
1618 			(w->attrib.width * slot->scale) / 2;
1619 		    slot->ty = slot->y - w->attrib.y -
1620 			(w->attrib.height * slot->scale) / 2;
1621 		}
1622 	    }
1623 
1624 	    if (!ss->moreAdjust)
1625 		break;
1626 	}
1627     }
1628 
1629     UNWRAP (ss, s, preparePaintScreen);
1630     (*s->preparePaintScreen) (s, msSinceLastPaint);
1631     WRAP (ss, s, preparePaintScreen, shiftPreparePaintScreen);
1632 }
1633 
1634 static Bool
canStackRelativeTo(CompWindow * w)1635 canStackRelativeTo (CompWindow *w)
1636 {
1637     if (w->attrib.override_redirect)
1638         return FALSE;
1639 
1640     if (!w->shaded && !w->pendingMaps)
1641     {
1642         if (w->attrib.map_state != IsViewable || w->mapNum == 0)
1643             return FALSE;
1644     }
1645 
1646     return TRUE;
1647 }
1648 
1649 static void
shiftDonePaintScreen(CompScreen * s)1650 shiftDonePaintScreen (CompScreen *s)
1651 {
1652     SHIFT_SCREEN (s);
1653     CompWindow *w;
1654 
1655     if (ss->state != ShiftStateNone)
1656     {
1657 	if (ss->moreAdjust)
1658 	{
1659 	    damageScreen (s);
1660 	}
1661 	else
1662 	{
1663 	    if (ss->state == ShiftStateIn)
1664 	    {
1665 		ss->state = ShiftStateNone;
1666 		shiftActivateEvent(s, FALSE);
1667 		for (w = s->windows; w; w = w->next)
1668 		{
1669 		    SHIFT_WINDOW (w);
1670 		    sw->active = FALSE;
1671 		}
1672 		damageScreen (s);
1673 	    }
1674 	    else if (ss->state == ShiftStateOut)
1675 		ss->state = ShiftStateSwitching;
1676 
1677 	    if (ss->moveAdjust)
1678 		damageScreen (s);
1679 	}
1680 
1681 	if (ss->state == ShiftStateFinish)
1682 	{
1683 	    CompWindow *w;
1684 
1685 	    CompWindow *pw = NULL;
1686 	    int i;
1687 
1688 	    ss->state = ShiftStateIn;
1689 	    ss->moreAdjust = TRUE;
1690 	    damageScreen (s);
1691 
1692 	    if (!ss->canceled && ss->mvTarget != 0)
1693 	    for (i = 0; i < ss->nSlots; i++)
1694 	    {
1695 		w = ss->drawSlots[i].w;
1696 		if (ss->drawSlots[i].slot->primary && canStackRelativeTo (w))
1697 		{
1698 		    if (pw)
1699 			restackWindowAbove (w,pw);
1700 		    pw = w;
1701 		}
1702 	    }
1703 
1704 	    if (!ss->canceled && ss->selectedWindow &&
1705 		!ss->selectedWindow->destroyed)
1706 	    {
1707 		sendWindowActivationRequest (s, ss->selectedWindow->id);
1708 	    }
1709 	}
1710 
1711 	if (ss->state == ShiftStateNone)
1712 	    switchActivateEvent (s, FALSE);
1713     }
1714 
1715     UNWRAP (ss, s, donePaintScreen);
1716     (*s->donePaintScreen) (s);
1717     WRAP (ss, s, donePaintScreen, shiftDonePaintScreen);
1718 }
1719 
1720 static void
shiftTerm(CompScreen * s,Bool cancel)1721 shiftTerm (CompScreen *s, Bool cancel)
1722 {
1723     SHIFT_SCREEN (s);
1724 
1725     if (ss->grabIndex)
1726     {
1727         removeScreenGrab (s, ss->grabIndex, 0);
1728         ss->grabIndex = 0;
1729     }
1730 
1731     if (ss->state != ShiftStateNone &&
1732     	ss->state != ShiftStateFinish &&
1733     	ss->state != ShiftStateIn)
1734     {
1735 
1736 	if (cancel && ss->mvTarget != 0)
1737 	{
1738 	    if (ss->nWindows - ss->mvTarget > ss->mvTarget)
1739 		ss->mvAdjust = -ss->mvTarget;
1740 	    else
1741 		ss->mvAdjust = ss->nWindows - ss->mvTarget;
1742 	    ss->moveAdjust = TRUE;
1743 	}
1744 
1745 	ss->moreAdjust = TRUE;
1746 	ss->state = ShiftStateFinish;
1747 	ss->canceled = cancel;
1748 	damageScreen (s);
1749     }
1750 }
1751 
1752 static Bool
shiftTerminate(CompDisplay * d,CompAction * action,CompActionState state,CompOption * option,int nOption)1753 shiftTerminate (CompDisplay     *d,
1754 	       CompAction      *action,
1755 	       CompActionState state,
1756 	       CompOption      *option,
1757 	       int	        nOption)
1758 {
1759     CompScreen *s;
1760     Window     xid;
1761 
1762     xid = getIntOptionNamed (option, nOption, "root", 0);
1763 
1764     for (s = d->screens; s; s = s->next)
1765     {
1766 	if (xid && s->root != xid)
1767 	    continue;
1768 
1769 	shiftTerm (s, (state & CompActionStateCancel));
1770 
1771 	if (state & CompActionStateTermButton)
1772 	    action->state &= ~CompActionStateTermButton;
1773 
1774 	if (state & CompActionStateTermKey)
1775 	    action->state &= ~CompActionStateTermKey;
1776     }
1777 
1778     return FALSE;
1779 }
1780 
1781 static Bool
shiftInitiateScreen(CompScreen * s,CompAction * action,CompActionState state,CompOption * option,int nOption)1782 shiftInitiateScreen (CompScreen      *s,
1783 		     CompAction      *action,
1784 		     CompActionState state,
1785 		     CompOption      *option,
1786 		     int	      nOption)
1787 {
1788     CompMatch *match;
1789     int       count;
1790 
1791     SHIFT_SCREEN (s);
1792 
1793     if (otherScreenGrabExist (s, "shift", NULL))
1794 	return FALSE;
1795 
1796     ss->currentMatch = shiftGetWindowMatch (s);
1797 
1798     match = getMatchOptionNamed (option, nOption, "match", NULL);
1799     if (match)
1800     {
1801 	matchFini (&ss->match);
1802 	matchInit (&ss->match);
1803 	if (matchCopy (&ss->match, match))
1804 	{
1805 	    matchUpdate (s->display, &ss->match);
1806 	    ss->currentMatch = &ss->match;
1807 	}
1808     }
1809 
1810     count = shiftCountWindows (s);
1811 
1812     if (count < 1)
1813 	return FALSE;
1814 
1815     if (!ss->grabIndex)
1816 	ss->grabIndex = pushScreenGrab (s, s->invisibleCursor, "shift");
1817 
1818 
1819     if (ss->grabIndex)
1820     {
1821 	ss->state = ShiftStateOut;
1822 	shiftActivateEvent(s, TRUE);
1823 
1824 	if (!shiftCreateWindowList (s))
1825 	    return FALSE;
1826 
1827 	ss->selectedWindow = ss->windows[0];
1828 	shiftRenderWindowTitle (s);
1829 	ss->mvTarget = 0;
1830 	ss->mvAdjust = 0;
1831 	ss->mvVelocity = 0;
1832 
1833     	ss->moreAdjust = TRUE;
1834 	damageScreen (s);
1835 
1836 	switchActivateEvent (s, TRUE);
1837     }
1838 
1839     ss->usedOutput = s->currentOutputDev;
1840 
1841     return TRUE;
1842 }
1843 
1844 static Bool
shiftDoSwitch(CompDisplay * d,CompAction * action,CompActionState state,CompOption * option,int nOption,Bool nextWindow,ShiftType type)1845 shiftDoSwitch (CompDisplay     *d,
1846 	      CompAction      *action,
1847 	      CompActionState state,
1848 	      CompOption      *option,
1849 	      int             nOption,
1850 	      Bool            nextWindow,
1851 	      ShiftType        type)
1852 {
1853     CompScreen *s;
1854     Window     xid;
1855     Bool       ret = TRUE;
1856     Bool       initial = FALSE;
1857 
1858     xid = getIntOptionNamed (option, nOption, "root", 0);
1859 
1860     s = findScreenAtDisplay (d, xid);
1861     if (s)
1862     {
1863 	SHIFT_SCREEN (s);
1864 
1865 	if ((ss->state == ShiftStateNone) || (ss->state == ShiftStateIn))
1866 	{
1867 	    if (type == ShiftTypeGroup)
1868 	    {
1869     		CompWindow *w;
1870     		w = findWindowAtDisplay (d, getIntOptionNamed (option, nOption,
1871     							       "window", 0));
1872     		if (w)
1873     		{
1874     		    ss->type = ShiftTypeGroup;
1875     		    ss->clientLeader =
1876 			(w->clientLeader) ? w->clientLeader : w->id;
1877 		    ret = shiftInitiateScreen (s, action, state, option,
1878 					       nOption);
1879 		}
1880 	    }
1881 	    else
1882 	    {
1883 		ss->type = type;
1884 		ret = shiftInitiateScreen (s, action, state, option, nOption);
1885 	    }
1886 
1887 	    if (state & CompActionStateInitKey)
1888 		action->state |= CompActionStateTermKey;
1889 
1890 	    if (state & CompActionStateInitButton)
1891 		action->state |= CompActionStateTermButton;
1892 
1893 	    if (state & CompActionStateInitEdge)
1894 		action->state |= CompActionStateTermEdge;
1895 
1896 	    initial = TRUE;
1897 	}
1898 
1899 	if (ret)
1900 	{
1901     	    switchToWindow (s, nextWindow);
1902 	    if (initial && FALSE)
1903 	    {
1904 		ss->mvTarget += ss->mvAdjust;
1905 		ss->mvAdjust  = 0.0;
1906 	    }
1907 	}
1908     }
1909 
1910     return ret;
1911 }
1912 
1913 static Bool
shiftInitiate(CompDisplay * d,CompAction * action,CompActionState state,CompOption * option,int nOption)1914 shiftInitiate (CompDisplay     *d,
1915 	       CompAction      *action,
1916 	       CompActionState state,
1917 	       CompOption      *option,
1918 	       int             nOption)
1919 {
1920     CompScreen *s;
1921     Window     xid;
1922     Bool       ret = TRUE;
1923 
1924     xid = getIntOptionNamed (option, nOption, "root", 0);
1925 
1926     s = findScreenAtDisplay (d, xid);
1927     if (s)
1928     {
1929 	SHIFT_SCREEN (s);
1930 
1931 	ss->type = ShiftTypeNormal;
1932 
1933 	if ((ss->state == ShiftStateNone) || (ss->state == ShiftStateIn) ||
1934 	    (ss->state == ShiftStateFinish))
1935 	    ret = shiftInitiateScreen (s, action, state, option, nOption);
1936 	else
1937 	    ret = shiftTerminate (d, action, state, option, nOption);
1938 
1939 	if (state & CompActionStateTermButton)
1940 	    action->state &= ~CompActionStateTermButton;
1941 
1942 	if (state & CompActionStateTermKey)
1943 	    action->state &= ~CompActionStateTermKey;
1944     }
1945 
1946     return ret;
1947 }
1948 
1949 static Bool
shiftInitiateAll(CompDisplay * d,CompAction * action,CompActionState state,CompOption * option,int nOption)1950 shiftInitiateAll (CompDisplay     *d,
1951 		  CompAction      *action,
1952 		  CompActionState state,
1953 		  CompOption      *option,
1954 		  int             nOption)
1955 {
1956     CompScreen *s;
1957     Window     xid;
1958     Bool       ret = TRUE;
1959 
1960     xid = getIntOptionNamed (option, nOption, "root", 0);
1961 
1962     s = findScreenAtDisplay (d, xid);
1963     if (s)
1964     {
1965 	SHIFT_SCREEN (s);
1966 
1967 	ss->type = ShiftTypeAll;
1968 
1969 	if ((ss->state == ShiftStateNone) || (ss->state == ShiftStateIn) ||
1970 	    (ss->state == ShiftStateFinish))
1971 	    ret = shiftInitiateScreen (s, action, state, option, nOption);
1972 	else
1973 	    ret = shiftTerminate (d, action, state, option, nOption);
1974 
1975 	if (state & CompActionStateTermButton)
1976 	    action->state &= ~CompActionStateTermButton;
1977 
1978 	if (state & CompActionStateTermKey)
1979 	    action->state &= ~CompActionStateTermKey;
1980     }
1981 
1982     return ret;
1983 }
1984 
1985 static Bool
shiftNext(CompDisplay * d,CompAction * action,CompActionState state,CompOption * option,int nOption)1986 shiftNext (CompDisplay     *d,
1987   	  CompAction      *action,
1988 	  CompActionState state,
1989 	  CompOption      *option,
1990 	  int	           nOption)
1991 {
1992     return shiftDoSwitch (d, action, state, option, nOption,
1993 			 TRUE, ShiftTypeNormal);
1994 }
1995 
1996 static Bool
shiftPrev(CompDisplay * d,CompAction * action,CompActionState state,CompOption * option,int nOption)1997 shiftPrev (CompDisplay     *d,
1998   	  CompAction      *action,
1999 	  CompActionState state,
2000 	  CompOption      *option,
2001 	  int	           nOption)
2002 {
2003     return shiftDoSwitch (d, action, state, option, nOption,
2004 			 FALSE, ShiftTypeNormal);
2005 }
2006 
2007 static Bool
shiftNextAll(CompDisplay * d,CompAction * action,CompActionState state,CompOption * option,int nOption)2008 shiftNextAll (CompDisplay     *d,
2009 	     CompAction      *action,
2010    	     CompActionState state,
2011    	     CompOption      *option,
2012    	     int	     nOption)
2013 {
2014     return shiftDoSwitch (d, action, state, option, nOption,
2015 			 TRUE, ShiftTypeAll);
2016 }
2017 
2018 static Bool
shiftPrevAll(CompDisplay * d,CompAction * action,CompActionState state,CompOption * option,int nOption)2019 shiftPrevAll (CompDisplay     *d,
2020 	     CompAction      *action,
2021    	     CompActionState state,
2022    	     CompOption      *option,
2023    	     int	     nOption)
2024 {
2025     return shiftDoSwitch (d, action, state, option, nOption,
2026 			 FALSE, ShiftTypeAll);
2027 }
2028 
2029 static Bool
shiftNextGroup(CompDisplay * d,CompAction * action,CompActionState state,CompOption * option,int nOption)2030 shiftNextGroup (CompDisplay     *d,
2031      	       CompAction      *action,
2032      	       CompActionState state,
2033      	       CompOption      *option,
2034      	       int	       nOption)
2035 {
2036     return shiftDoSwitch (d, action, state, option, nOption,
2037 			 TRUE, ShiftTypeGroup);
2038 }
2039 
2040 static Bool
shiftPrevGroup(CompDisplay * d,CompAction * action,CompActionState state,CompOption * option,int nOption)2041 shiftPrevGroup (CompDisplay     *d,
2042      	       CompAction      *action,
2043      	       CompActionState state,
2044      	       CompOption      *option,
2045      	       int	       nOption)
2046 {
2047     return shiftDoSwitch (d, action, state, option, nOption,
2048 			 FALSE, ShiftTypeGroup);
2049 }
2050 
2051 
2052 static void
shiftWindowRemove(CompDisplay * d,CompWindow * w)2053 shiftWindowRemove (CompDisplay * d,
2054 		   CompWindow  *w)
2055 {
2056     if (w)
2057     {
2058 	Bool inList = FALSE;
2059 	int j, i = 0;
2060 	CompWindow *selected;
2061 
2062 	SHIFT_SCREEN(w->screen);
2063 
2064 	if (ss->state == ShiftStateNone)
2065 	    return;
2066 
2067 	if (isShiftWin(w))
2068     	    return;
2069 
2070 	selected = ss->selectedWindow;
2071 
2072 	while (i < ss->nWindows)
2073 	{
2074 	    if (w == ss->windows[i])
2075 	    {
2076 		inList = TRUE;
2077 
2078 		if (w == selected)
2079 		{
2080 		    if (i < (ss->nWindows - 1))
2081 			selected = ss->windows[i + 1];
2082 		    else
2083 			selected = ss->windows[0];
2084 
2085 		    ss->selectedWindow = selected;
2086 		    shiftRenderWindowTitle (w->screen);
2087 		}
2088 
2089 		ss->nWindows--;
2090 		for (j = i; j < ss->nWindows; j++)
2091 		    ss->windows[j] = ss->windows[j + 1];
2092 	    }
2093 	    else
2094 	    {
2095 		i++;
2096 	    }
2097 	}
2098 
2099 	if (!inList)
2100 	    return;
2101 
2102 	if (ss->nWindows == 0)
2103 	{
2104 	    CompOption o;
2105 
2106 	    o.type = CompOptionTypeInt;
2107 	    o.name = "root";
2108 	    o.value.i = w->screen->root;
2109 
2110 	    shiftTerminate (d, NULL, 0, &o, 1);
2111 	    return;
2112 	}
2113 
2114 	// Let the window list be updated to avoid crash
2115 	// when a window is closed while ending shift (ShiftStateIn).
2116 	if (!ss->grabIndex && ss->state != ShiftStateIn)
2117 	    return;
2118 
2119 	if (shiftUpdateWindowList (w->screen))
2120 	{
2121 	    ss->moreAdjust = TRUE;
2122 	    ss->state = ShiftStateOut;
2123 	    damageScreen (w->screen);
2124 	}
2125     }
2126 }
2127 
2128 static void
shiftHandleEvent(CompDisplay * d,XEvent * event)2129 shiftHandleEvent (CompDisplay *d,
2130 		 XEvent      *event)
2131 {
2132     SHIFT_DISPLAY (d);
2133     CompScreen *s;
2134     CompWindow *w = NULL;
2135 
2136     switch (event->type) {
2137     case DestroyNotify:
2138 	/* We need to get the CompWindow * for event->xdestroywindow.window
2139 	   here because in the (*d->handleEvent) call below, that CompWindow's
2140 	   id will become 1, so findWindowAtDisplay won't be able to find the
2141 	   CompWindow after that. */
2142 	w = findWindowAtDisplay (d, event->xdestroywindow.window);
2143 	break;
2144     }
2145 
2146     UNWRAP (sd, d, handleEvent);
2147     (*d->handleEvent) (d, event);
2148     WRAP (sd, d, handleEvent, shiftHandleEvent);
2149 
2150     switch (event->type) {
2151     case PropertyNotify:
2152 	if (event->xproperty.atom == XA_WM_NAME)
2153 	{
2154 	    w = findWindowAtDisplay (d, event->xproperty.window);
2155 	    if (w)
2156 	    {
2157     		SHIFT_SCREEN (w->screen);
2158     		if (ss->grabIndex && (w == ss->selectedWindow))
2159     		{
2160     		    shiftRenderWindowTitle (w->screen);
2161     		    damageScreen (w->screen);
2162 		}
2163 	    }
2164 	}
2165 	break;
2166     case UnmapNotify:
2167 	w = findWindowAtDisplay (d, event->xunmap.window);
2168 	shiftWindowRemove (d, w);
2169 	break;
2170     case DestroyNotify:
2171 	shiftWindowRemove (d, w);
2172 	break;
2173     case KeyPress:
2174 	s = findScreenAtDisplay (d, event->xkey.root);
2175 
2176 	if (s)
2177 	{
2178 	    SHIFT_SCREEN (s);
2179 
2180 	    if (ss->state == ShiftStateSwitching)
2181 	    {
2182 		if (event->xkey.keycode == sd->leftKey)
2183 		    switchToWindow (s, FALSE);
2184 		else if (event->xkey.keycode == sd->rightKey)
2185 		    switchToWindow (s, TRUE);
2186 		else if (event->xkey.keycode == sd->upKey)
2187 		    switchToWindow (s, FALSE);
2188 		else if (event->xkey.keycode == sd->downKey)
2189 		    switchToWindow (s, TRUE);
2190 	    }
2191 	}
2192 
2193 	break;
2194     case ButtonPress:
2195 	s = findScreenAtDisplay (d, event->xbutton.root);
2196 
2197 	if (s)
2198 	{
2199 	    SHIFT_SCREEN (s);
2200 
2201 	    if (ss->state == ShiftStateSwitching || ss->state == ShiftStateOut)
2202 	    {
2203 		if (event->xbutton.button == Button5)
2204 		    switchToWindow (s, FALSE);
2205 		else if (event->xbutton.button == Button4)
2206 		    switchToWindow (s, TRUE);
2207 		if (event->xbutton.button == Button1)
2208 		{
2209 		    ss->buttonPressTime = event->xbutton.time;
2210 		    ss->buttonPressed   = TRUE;
2211 		    ss->startX          = event->xbutton.x_root;
2212 		    ss->startY          = event->xbutton.y_root;
2213 		    ss->startTarget     = ss->mvTarget + ss->mvAdjust;
2214 		}
2215 	    }
2216 	}
2217 	break;
2218     case ButtonRelease:
2219 	s = findScreenAtDisplay (d, event->xbutton.root);
2220 
2221 	if (s)
2222 	{
2223 	    SHIFT_SCREEN (s);
2224 
2225 	    if (ss->state == ShiftStateSwitching || ss->state == ShiftStateOut)
2226 	    {
2227 		if (event->xbutton.button == Button1 && ss->buttonPressed)
2228 		{
2229 		    int new;
2230 		    if ((int)(event->xbutton.time - ss->buttonPressTime) <
2231 		        shiftGetClickDuration (s))
2232 		    	shiftTerm (s, FALSE);
2233 
2234 		    ss->buttonPressTime = 0;
2235 		    ss->buttonPressed   = FALSE;
2236 
2237 		    if (ss->mvTarget - floor (ss->mvTarget) >= 0.5)
2238 		    {
2239 			ss->mvAdjust = ceil(ss->mvTarget) - ss->mvTarget;
2240 			new = ceil(ss->mvTarget);
2241 		    }
2242 		    else
2243 		    {
2244 			ss->mvAdjust = floor(ss->mvTarget) - ss->mvTarget;
2245 			new = floor(ss->mvTarget);
2246 		    }
2247 
2248 		    while (new < 0)
2249 			new += ss->nWindows;
2250 		    new = new % ss->nWindows;
2251 
2252 		    ss->selectedWindow = ss->windows[new];
2253 
2254 		    shiftRenderWindowTitle (s);
2255 		    ss->moveAdjust = TRUE;
2256 		    damageScreen(s);
2257 		}
2258 
2259 	    }
2260 	}
2261 	break;
2262     case MotionNotify:
2263 	s = findScreenAtDisplay (d, event->xbutton.root);
2264 
2265 	if (s)
2266 	{
2267 	    SHIFT_SCREEN (s);
2268 
2269 	    if (ss->state == ShiftStateSwitching || ss->state == ShiftStateOut)
2270 	    {
2271 		if (ss->buttonPressed)
2272 		{
2273 		    int ox1 = s->outputDev[ss->usedOutput].region.extents.x1;
2274 		    int ox2 = s->outputDev[ss->usedOutput].region.extents.x2;
2275 		    int oy1 = s->outputDev[ss->usedOutput].region.extents.y1;
2276 		    int oy2 = s->outputDev[ss->usedOutput].region.extents.y2;
2277 
2278 		    float div = 0;
2279 		    int   wx  = 0;
2280 		    int   wy  = 0;
2281 		    int   new;
2282 
2283 		    switch (shiftGetMode (s))
2284 		    {
2285 		    case ModeCover:
2286 			div = event->xmotion.x_root - ss->startX;
2287 			div /= (ox2 - ox1) / shiftGetMouseSpeed (s);
2288 			break;
2289 		    case ModeFlip:
2290 			div = event->xmotion.y_root - ss->startY;
2291 			div /= (oy2 - oy1) / shiftGetMouseSpeed (s);
2292 			break;
2293 		    }
2294 
2295 		    ss->mvTarget = ss->startTarget + div - ss->mvAdjust;
2296 		    ss->moveAdjust = TRUE;
2297 		    while (ss->mvTarget >= ss->nWindows)
2298 		    {
2299 			ss->mvTarget -= ss->nWindows;
2300 			ss->invert = !ss->invert;
2301 		    }
2302 
2303 		    while (ss->mvTarget < 0)
2304 		    {
2305 			ss->mvTarget += ss->nWindows;
2306 			ss->invert = !ss->invert;
2307 		    }
2308 
2309 		    if (ss->mvTarget - floor (ss->mvTarget) >= 0.5)
2310 			new = ceil(ss->mvTarget);
2311 		    else
2312 			new = floor(ss->mvTarget);
2313 
2314 		    while (new < 0)
2315 			new += ss->nWindows;
2316 		    new = new % ss->nWindows;
2317 
2318 		    if (ss->selectedWindow != ss->windows[new])
2319 		    {
2320 			ss->selectedWindow = ss->windows[new];
2321 			shiftRenderWindowTitle (s);
2322 		    }
2323 
2324 		    if (event->xmotion.x_root < 50)
2325 			wx = 50;
2326 		    if (s->width - event->xmotion.x_root < 50)
2327 			wx = -50;
2328 		    if (event->xmotion.y_root < 50)
2329 			wy = 50;
2330 		    if (s->height - event->xmotion.y_root < 50)
2331 			wy = -50;
2332 		    if (wx != 0 || wy != 0)
2333 		    {
2334 		    	warpPointer (s, wx, wy);
2335 			ss->startX += wx;
2336 			ss->startY += wy;
2337 		    }
2338 
2339 		    damageScreen(s);
2340 		}
2341 
2342 	    }
2343 	}
2344 	break;
2345     }
2346 }
2347 
2348 static Bool
shiftDamageWindowRect(CompWindow * w,Bool initial,BoxPtr rect)2349 shiftDamageWindowRect (CompWindow *w,
2350 		      Bool	  initial,
2351 		      BoxPtr     rect)
2352 {
2353     Bool status = FALSE;
2354 
2355     SHIFT_SCREEN (w->screen);
2356 
2357     if (initial)
2358     {
2359 	if (ss->grabIndex && isShiftWin (w))
2360 	{
2361 	    shiftAddWindowToList (w->screen, w);
2362 	    if (shiftUpdateWindowList (w->screen))
2363 	    {
2364 		SHIFT_WINDOW (w);
2365 
2366     		sw->active = TRUE;
2367 		ss->moreAdjust = TRUE;
2368 		ss->state = ShiftStateOut;
2369 		damageScreen (w->screen);
2370 	    }
2371 	}
2372     }
2373     else if (ss->state == ShiftStateSwitching)
2374     {
2375 	SHIFT_WINDOW (w);
2376 
2377 	if (sw->active)
2378 	{
2379 	    damageScreen (w->screen);
2380 	    status = TRUE;
2381 	}
2382     }
2383 
2384     UNWRAP (ss, w->screen, damageWindowRect);
2385     status |= (*w->screen->damageWindowRect) (w, initial, rect);
2386     WRAP (ss, w->screen, damageWindowRect, shiftDamageWindowRect);
2387 
2388     return status;
2389 }
2390 
2391 static Bool
shiftInitDisplay(CompPlugin * p,CompDisplay * d)2392 shiftInitDisplay (CompPlugin  *p,
2393 		 CompDisplay *d)
2394 {
2395     ShiftDisplay *sd;
2396     int          index;
2397 
2398     if (!checkPluginABI ("core", CORE_ABIVERSION))
2399 	return FALSE;
2400 
2401     sd = malloc (sizeof (ShiftDisplay));
2402     if (!sd)
2403 	return FALSE;
2404 
2405     sd->screenPrivateIndex = allocateScreenPrivateIndex (d);
2406     if (sd->screenPrivateIndex < 0)
2407     {
2408 	free (sd);
2409 	return FALSE;
2410     }
2411 
2412     if (checkPluginABI ("text", TEXT_ABIVERSION) &&
2413 	getPluginDisplayIndex (d, "text", &index))
2414     {
2415 	sd->textFunc = d->base.privates[index].ptr;
2416     }
2417     else
2418     {
2419 	compLogMessage ("shift", CompLogLevelWarn,
2420 			"No compatible text plugin loaded.");
2421 	sd->textFunc = NULL;
2422     }
2423 
2424     sd->leftKey  = XKeysymToKeycode (d->display, XStringToKeysym ("Left"));
2425     sd->rightKey = XKeysymToKeycode (d->display, XStringToKeysym ("Right"));
2426     sd->upKey    = XKeysymToKeycode (d->display, XStringToKeysym ("Up"));
2427     sd->downKey  = XKeysymToKeycode (d->display, XStringToKeysym ("Down"));
2428 
2429     shiftSetInitiateKeyInitiate (d, shiftInitiate);
2430     shiftSetInitiateKeyTerminate (d, shiftTerminate);
2431     shiftSetInitiateAllKeyInitiate (d, shiftInitiateAll);
2432     shiftSetInitiateAllKeyTerminate (d, shiftTerminate);
2433     shiftSetNextKeyInitiate (d, shiftNext);
2434     shiftSetNextKeyTerminate (d, shiftTerminate);
2435     shiftSetPrevKeyInitiate (d, shiftPrev);
2436     shiftSetPrevKeyTerminate (d, shiftTerminate);
2437     shiftSetNextAllKeyInitiate (d, shiftNextAll);
2438     shiftSetNextAllKeyTerminate (d, shiftTerminate);
2439     shiftSetPrevAllKeyInitiate (d, shiftPrevAll);
2440     shiftSetPrevAllKeyTerminate (d, shiftTerminate);
2441     shiftSetNextGroupKeyInitiate (d, shiftNextGroup);
2442     shiftSetNextGroupKeyTerminate (d, shiftTerminate);
2443     shiftSetPrevGroupKeyInitiate (d, shiftPrevGroup);
2444     shiftSetPrevGroupKeyTerminate (d, shiftTerminate);
2445 
2446     shiftSetInitiateButtonInitiate (d, shiftInitiate);
2447     shiftSetInitiateButtonTerminate (d, shiftTerminate);
2448     shiftSetInitiateAllButtonInitiate (d, shiftInitiateAll);
2449     shiftSetInitiateAllButtonTerminate (d, shiftTerminate);
2450     shiftSetNextButtonInitiate (d, shiftNext);
2451     shiftSetNextButtonTerminate (d, shiftTerminate);
2452     shiftSetPrevButtonInitiate (d, shiftPrev);
2453     shiftSetPrevButtonTerminate (d, shiftTerminate);
2454     shiftSetNextAllButtonInitiate (d, shiftNextAll);
2455     shiftSetNextAllButtonTerminate (d, shiftTerminate);
2456     shiftSetPrevAllButtonInitiate (d, shiftPrevAll);
2457     shiftSetPrevAllButtonTerminate (d, shiftTerminate);
2458     shiftSetNextGroupButtonInitiate (d, shiftNextGroup);
2459     shiftSetNextGroupButtonTerminate (d, shiftTerminate);
2460     shiftSetPrevGroupButtonInitiate (d, shiftPrevGroup);
2461     shiftSetPrevGroupButtonTerminate (d, shiftTerminate);
2462 
2463     shiftSetInitiateEdgeInitiate (d, shiftInitiate);
2464     shiftSetInitiateEdgeTerminate (d, shiftTerminate);
2465     shiftSetInitiateAllEdgeInitiate (d, shiftInitiateAll);
2466     shiftSetInitiateAllEdgeTerminate (d, shiftTerminate);
2467 
2468     shiftSetTerminateButtonInitiate (d, shiftTerminate);
2469 
2470     WRAP (sd, d, handleEvent, shiftHandleEvent);
2471 
2472     d->base.privates[displayPrivateIndex].ptr = sd;
2473 
2474     return TRUE;
2475 }
2476 
2477 static void
shiftFiniDisplay(CompPlugin * p,CompDisplay * d)2478 shiftFiniDisplay (CompPlugin  *p,
2479 		 CompDisplay *d)
2480 {
2481     SHIFT_DISPLAY (d);
2482 
2483     freeScreenPrivateIndex (d, sd->screenPrivateIndex);
2484 
2485     UNWRAP (sd, d, handleEvent);
2486 
2487     free (sd);
2488 }
2489 
2490 static Bool
shiftInitScreen(CompPlugin * p,CompScreen * s)2491 shiftInitScreen (CompPlugin *p,
2492 		CompScreen *s)
2493 {
2494     ShiftScreen *ss;
2495 
2496     SHIFT_DISPLAY (s->display);
2497 
2498     ss = malloc (sizeof (ShiftScreen));
2499     if (!ss)
2500 	return FALSE;
2501 
2502     ss->windowPrivateIndex = allocateWindowPrivateIndex (s);
2503     if (ss->windowPrivateIndex < 0)
2504     {
2505 	free (ss);
2506 	return FALSE;
2507     }
2508 
2509     ss->grabIndex = 0;
2510 
2511     ss->state = ShiftStateNone;
2512 
2513     ss->windows = NULL;
2514     ss->windowsSize = 0;
2515 
2516     ss->drawSlots = NULL;
2517     ss->slotsSize = 0;
2518 
2519     ss->activeSlot = NULL;
2520 
2521     ss->selectedWindow = NULL;
2522 
2523     ss->moreAdjust   = FALSE;
2524 
2525     ss->usedOutput = 0;
2526 
2527     ss->mvAdjust = 0;
2528     ss->mvVelocity = 0;
2529     ss->mvTarget = 0;
2530     ss->invert = FALSE;
2531 
2532     ss->textData = NULL;
2533 
2534     ss->anim         = 0.0;
2535     ss->animVelocity = 0.0;
2536 
2537     ss->buttonPressed = FALSE;
2538 
2539     matchInit (&ss->match);
2540 
2541     WRAP (ss, s, preparePaintScreen, shiftPreparePaintScreen);
2542     WRAP (ss, s, donePaintScreen, shiftDonePaintScreen);
2543     WRAP (ss, s, paintScreen, shiftPaintScreen);
2544     WRAP (ss, s, paintOutput, shiftPaintOutput);
2545     WRAP (ss, s, paintWindow, shiftPaintWindow);
2546     WRAP (ss, s, damageWindowRect, shiftDamageWindowRect);
2547 
2548     ss->cursor = XCreateFontCursor (s->display->display, XC_left_ptr);
2549 
2550     s->base.privates[sd->screenPrivateIndex].ptr = ss;
2551 
2552     return TRUE;
2553 }
2554 
2555 static void
shiftFiniScreen(CompPlugin * p,CompScreen * s)2556 shiftFiniScreen (CompPlugin *p,
2557 		CompScreen *s)
2558 {
2559     SHIFT_SCREEN (s);
2560 
2561     freeWindowPrivateIndex (s, ss->windowPrivateIndex);
2562 
2563     UNWRAP (ss, s, preparePaintScreen);
2564     UNWRAP (ss, s, donePaintScreen);
2565     UNWRAP (ss, s, paintScreen);
2566     UNWRAP (ss, s, paintOutput);
2567     UNWRAP (ss, s, paintWindow);
2568     UNWRAP (ss, s, damageWindowRect);
2569 
2570     matchFini (&ss->match);
2571 
2572     shiftFreeWindowTitle (s);
2573 
2574     XFreeCursor (s->display->display, ss->cursor);
2575 
2576     if (ss->windows)
2577 	free (ss->windows);
2578 
2579     if (ss->drawSlots)
2580 	free (ss->drawSlots);
2581 
2582     free (ss);
2583 }
2584 
2585 static Bool
shiftInitWindow(CompPlugin * p,CompWindow * w)2586 shiftInitWindow (CompPlugin *p,
2587 		CompWindow *w)
2588 {
2589     ShiftWindow *sw;
2590 
2591     SHIFT_SCREEN (w->screen);
2592 
2593     sw = calloc (1, sizeof (ShiftWindow));
2594     if (!sw)
2595 	return FALSE;
2596 
2597     sw->slots[0].scale = 1.0;
2598     sw->slots[1].scale = 1.0;
2599 
2600     sw->brightness = 1.0;
2601     sw->opacity    = 1.0;
2602 
2603     w->base.privates[ss->windowPrivateIndex].ptr = sw;
2604 
2605     return TRUE;
2606 }
2607 
2608 static void
shiftFiniWindow(CompPlugin * p,CompWindow * w)2609 shiftFiniWindow (CompPlugin *p,
2610 		CompWindow *w)
2611 {
2612     SHIFT_WINDOW (w);
2613 
2614     free (sw);
2615 }
2616 
2617 static Bool
shiftInit(CompPlugin * p)2618 shiftInit (CompPlugin *p)
2619 {
2620     displayPrivateIndex = allocateDisplayPrivateIndex ();
2621     if (displayPrivateIndex < 0)
2622 	return FALSE;
2623 
2624     return TRUE;
2625 }
2626 
2627 static void
shiftFini(CompPlugin * p)2628 shiftFini (CompPlugin *p)
2629 {
2630     if (displayPrivateIndex >= 0)
2631 	freeDisplayPrivateIndex (displayPrivateIndex);
2632 }
2633 
2634 static CompBool
shiftInitObject(CompPlugin * p,CompObject * o)2635 shiftInitObject (CompPlugin *p,
2636 		 CompObject *o)
2637 {
2638     static InitPluginObjectProc dispTab[] = {
2639 	(InitPluginObjectProc) 0, /* InitCore */
2640 	(InitPluginObjectProc) shiftInitDisplay,
2641 	(InitPluginObjectProc) shiftInitScreen,
2642 	(InitPluginObjectProc) shiftInitWindow
2643     };
2644 
2645     RETURN_DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), TRUE, (p, o));
2646 }
2647 
2648 static void
shiftFiniObject(CompPlugin * p,CompObject * o)2649 shiftFiniObject (CompPlugin *p,
2650 		 CompObject *o)
2651 {
2652     static FiniPluginObjectProc dispTab[] = {
2653 	(FiniPluginObjectProc) 0, /* FiniCore */
2654 	(FiniPluginObjectProc) shiftFiniDisplay,
2655 	(FiniPluginObjectProc) shiftFiniScreen,
2656 	(FiniPluginObjectProc) shiftFiniWindow
2657     };
2658 
2659     DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), (p, o));
2660 }
2661 
2662 CompPluginVTable shiftVTable = {
2663     "shift",
2664     0,
2665     shiftInit,
2666     shiftFini,
2667     shiftInitObject,
2668     shiftFiniObject,
2669     0,
2670     0
2671 };
2672 
2673 CompPluginVTable *
getCompPluginInfo(void)2674 getCompPluginInfo (void)
2675 {
2676     return &shiftVTable;
2677 }
2678