1 /*
2  * Copyright © 2005 Novell, Inc.
3  *
4  * Permission to use, copy, modify, distribute, and sell this software
5  * and its documentation for any purpose is hereby granted without
6  * fee, provided that the above copyright notice appear in all copies
7  * and that both that copyright notice and this permission notice
8  * appear in supporting documentation, and that the name of
9  * Novell, Inc. not be used in advertising or publicity pertaining to
10  * distribution of the software without specific, written prior permission.
11  * Novell, Inc. makes no representations about the suitability of this
12  * software for any purpose. It is provided "as is" without express or
13  * implied warranty.
14  *
15  * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
17  * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR
18  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
19  * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
20  * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
21  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  *
23  * Author: David Reveman <davidr@novell.com>
24  */
25 
26 #include <X11/Xatom.h>
27 
28 #include <stdlib.h>
29 #include <string.h>
30 #include <math.h>
31 
32 #include <compiz-core.h>
33 
34 static CompMetadata minMetadata;
35 
36 static int displayPrivateIndex;
37 
38 typedef struct _MinDisplay {
39     int		    screenPrivateIndex;
40     HandleEventProc handleEvent;
41     Atom	    winChangeStateAtom;
42 } MinDisplay;
43 
44 #define MIN_SCREEN_OPTION_SPEED		   0
45 #define MIN_SCREEN_OPTION_TIMESTEP	   1
46 #define MIN_SCREEN_OPTION_WINDOW_MATCH	   2
47 #define MIN_SCREEN_OPTION_SHADE_RESISTANCE 3
48 #define MIN_SCREEN_OPTION_NUM		   4
49 
50 typedef struct _MinScreen {
51     int	windowPrivateIndex;
52 
53     CompOption opt[MIN_SCREEN_OPTION_NUM];
54 
55     PreparePaintScreenProc preparePaintScreen;
56     DonePaintScreenProc    donePaintScreen;
57     PaintOutputProc        paintOutput;
58     PaintWindowProc        paintWindow;
59     DamageWindowRectProc   damageWindowRect;
60     FocusWindowProc	   focusWindow;
61 
62     int shadeStep;
63     int moreAdjust;
64 } MinScreen;
65 
66 typedef struct _MinWindow {
67     GLfloat xVelocity, yVelocity, xScaleVelocity, yScaleVelocity;
68     GLfloat xScale, yScale;
69     GLfloat tx, ty;
70 
71     Bool adjust;
72 
73     int state, newState;
74 
75     int    shade;
76     Region region;
77 
78     int unmapCnt;
79 
80     Bool ignoreDamage;
81 } MinWindow;
82 
83 #define GET_MIN_DISPLAY(d)					 \
84     ((MinDisplay *) (d)->base.privates[displayPrivateIndex].ptr)
85 
86 #define MIN_DISPLAY(d)			 \
87     MinDisplay *md = GET_MIN_DISPLAY (d)
88 
89 #define GET_MIN_SCREEN(s, md)					     \
90     ((MinScreen *) (s)->base.privates[(md)->screenPrivateIndex].ptr)
91 
92 #define MIN_SCREEN(s)						     \
93     MinScreen *ms = GET_MIN_SCREEN (s, GET_MIN_DISPLAY (s->display))
94 
95 #define GET_MIN_WINDOW(w, ms)					     \
96     ((MinWindow *) (w)->base.privates[(ms)->windowPrivateIndex].ptr)
97 
98 #define MIN_WINDOW(w)					   \
99     MinWindow *mw = GET_MIN_WINDOW  (w,			   \
100 		    GET_MIN_SCREEN  (w->screen,		   \
101 		    GET_MIN_DISPLAY (w->screen->display)))
102 
103 #define NUM_OPTIONS(s) (sizeof ((s)->opt) / sizeof (CompOption))
104 
105 static CompOption *
minGetScreenOptions(CompPlugin * plugin,CompScreen * screen,int * count)106 minGetScreenOptions (CompPlugin *plugin,
107 		     CompScreen *screen,
108 		     int	*count)
109 {
110     MIN_SCREEN (screen);
111 
112     *count = NUM_OPTIONS (ms);
113     return ms->opt;
114 }
115 
116 static Bool
minSetScreenOption(CompPlugin * plugin,CompScreen * screen,const char * name,CompOptionValue * value)117 minSetScreenOption (CompPlugin      *plugin,
118 		    CompScreen      *screen,
119 		    const char	    *name,
120 		    CompOptionValue *value)
121 {
122     CompOption *o;
123     int	       index;
124 
125     MIN_SCREEN (screen);
126 
127     o = compFindOption (ms->opt, NUM_OPTIONS (ms), name, &index);
128     if (!o)
129 	return FALSE;
130 
131     switch (index) {
132     case MIN_SCREEN_OPTION_SHADE_RESISTANCE:
133 	if (compSetIntOption (o, value))
134 	{
135 	    if (o->value.i)
136 		ms->shadeStep = o->rest.i.max - o->value.i + 1;
137 	    else
138 		ms->shadeStep = 0;
139 
140 	    return TRUE;
141 	}
142 	break;
143     default:
144 	if (compSetOption (o, value))
145 	    return TRUE;
146 	break;
147     }
148 
149     return FALSE;
150 }
151 
152 static void
minSetShade(CompWindow * w,int shade)153 minSetShade (CompWindow *w,
154 	     int	shade)
155 {
156     REGION rect;
157     int	   h = w->attrib.height + w->attrib.border_width * 2;
158 
159     MIN_WINDOW (w);
160 
161     EMPTY_REGION (w->region);
162 
163     rect.rects = &rect.extents;
164     rect.numRects = rect.size = 1;
165 
166     w->height = shade;
167 
168     rect.extents.x1 = 0;
169     rect.extents.y1 = h - shade;
170     rect.extents.x2 = w->width;
171     rect.extents.y2 = h;
172 
173     XIntersectRegion (mw->region, &rect, w->region);
174     XOffsetRegion (w->region, w->attrib.x, w->attrib.y - (h - shade));
175 
176     w->matrix = w->texture->matrix;
177     w->matrix.x0 -= (w->attrib.x * w->matrix.xx);
178     w->matrix.y0 -= ((w->attrib.y - (h - shade)) * w->matrix.yy);
179 
180     (*w->screen->windowResizeNotify) (w, 0, 0, 0, 0);
181 }
182 
183 static int
minGetWindowState(CompWindow * w)184 minGetWindowState (CompWindow *w)
185 {
186     Atom	  actual;
187     int		  result, format;
188     unsigned long n, left;
189     unsigned char *data;
190     int           retval = WithdrawnState;
191 
192     result = XGetWindowProperty (w->screen->display->display, w->id,
193 				 w->screen->display->wmStateAtom, 0L, 1L, FALSE,
194 				 w->screen->display->wmStateAtom,
195 				 &actual, &format, &n, &left, &data);
196 
197     if (result == Success && data)
198     {
199 	if (n)
200 	    memcpy (&retval, data, sizeof (int));
201 
202 	XFree ((void *) data);
203     }
204 
205     return retval;
206 }
207 
208 static int
adjustMinVelocity(CompWindow * w)209 adjustMinVelocity (CompWindow *w)
210 {
211     float dx, dy, dxs, dys, adjust, amount;
212     float x1, y1, xScale, yScale;
213 
214     MIN_WINDOW (w);
215 
216     if (mw->newState == IconicState)
217     {
218 	x1 = w->iconGeometry.x;
219 	y1 = w->iconGeometry.y;
220 	xScale = (float) w->iconGeometry.width  / w->width;
221 	yScale = (float) w->iconGeometry.height / w->height;
222     }
223     else
224     {
225 	x1 = w->serverX;
226 	y1 = w->serverY;
227 	xScale = yScale = 1.0f;
228     }
229 
230     dx = x1 - (w->attrib.x + mw->tx);
231 
232     adjust = dx * 0.15f;
233     amount = fabs (dx) * 1.5f;
234     if (amount < 0.5f)
235 	amount = 0.5f;
236     else if (amount > 5.0f)
237 	amount = 5.0f;
238 
239     mw->xVelocity = (amount * mw->xVelocity + adjust) / (amount + 1.0f);
240 
241     dy = y1 - (w->attrib.y + mw->ty);
242 
243     adjust = dy * 0.15f;
244     amount = fabs (dy) * 1.5f;
245     if (amount < 0.5f)
246 	amount = 0.5f;
247     else if (amount > 5.0f)
248 	amount = 5.0f;
249 
250     mw->yVelocity = (amount * mw->yVelocity + adjust) / (amount + 1.0f);
251 
252     dxs = xScale - mw->xScale;
253 
254     adjust = dxs * 0.15f;
255     amount = fabs (dxs) * 10.0f;
256     if (amount < 0.01f)
257 	amount = 0.01f;
258     else if (amount > 0.15f)
259 	amount = 0.15f;
260 
261     mw->xScaleVelocity = (amount * mw->xScaleVelocity + adjust) /
262 	(amount + 1.0f);
263 
264     dys = yScale - mw->yScale;
265 
266     adjust = dys * 0.15f;
267     amount = fabs (dys) * 10.0f;
268     if (amount < 0.01f)
269 	amount = 0.01f;
270     else if (amount > 0.15f)
271 	amount = 0.15f;
272 
273     mw->yScaleVelocity = (amount * mw->yScaleVelocity + adjust) /
274 	(amount + 1.0f);
275 
276     if (fabs (dx) < 0.1f    && fabs (mw->xVelocity)      < 0.2f   &&
277 	fabs (dy) < 0.1f    && fabs (mw->yVelocity)      < 0.2f   &&
278 	fabs (dxs) < 0.001f && fabs (mw->xScaleVelocity) < 0.002f &&
279 	fabs (dys) < 0.001f && fabs (mw->yScaleVelocity) < 0.002f)
280     {
281 	mw->xVelocity = mw->yVelocity = mw->xScaleVelocity =
282 	    mw->yScaleVelocity = 0.0f;
283 	mw->tx = x1 - w->attrib.x;
284 	mw->ty = y1 - w->attrib.y;
285 	mw->xScale = xScale;
286 	mw->yScale = yScale;
287 
288 	return 0;
289     }
290 
291     return 1;
292 }
293 
294 static void
minPreparePaintScreen(CompScreen * s,int msSinceLastPaint)295 minPreparePaintScreen (CompScreen *s,
296 		       int	  msSinceLastPaint)
297 {
298     MIN_SCREEN (s);
299 
300     if (ms->moreAdjust)
301     {
302 	CompWindow *w;
303 	int        steps, h;
304 	float      amount, chunk;
305 
306 	amount = msSinceLastPaint * 0.05f *
307 	    ms->opt[MIN_SCREEN_OPTION_SPEED].value.f;
308 	steps  = amount / (0.5f * ms->opt[MIN_SCREEN_OPTION_TIMESTEP].value.f);
309 	if (!steps) steps = 1;
310 	chunk  = amount / (float) steps;
311 
312 	while (steps--)
313 	{
314 	    ms->moreAdjust = 0;
315 
316 	    for (w = s->windows; w; w = w->next)
317 	    {
318 		MIN_WINDOW (w);
319 
320 		if (mw->adjust)
321 		{
322 		    mw->adjust = adjustMinVelocity (w);
323 
324 		    ms->moreAdjust |= mw->adjust;
325 
326 		    mw->tx += mw->xVelocity * chunk;
327 		    mw->ty += mw->yVelocity * chunk;
328 		    mw->xScale += mw->xScaleVelocity * chunk;
329 		    mw->yScale += mw->yScaleVelocity * chunk;
330 
331 		    if (!mw->adjust)
332 		    {
333 			mw->state = mw->newState;
334 
335 			mw->ignoreDamage = TRUE;
336 			while (mw->unmapCnt)
337 			{
338 			    unmapWindow (w);
339 			    mw->unmapCnt--;
340 			}
341 			mw->ignoreDamage = FALSE;
342 		    }
343 		}
344 		else if (mw->region && w->damaged)
345 		{
346 		    if (w->shaded)
347 		    {
348 			if (mw->shade > 0)
349 			{
350 			    mw->shade -= (chunk * ms->shadeStep) + 1;
351 
352 			    if (mw->shade > 0)
353 			    {
354 				ms->moreAdjust = TRUE;
355 			    }
356 			    else
357 			    {
358 				mw->shade = 0;
359 
360 				mw->ignoreDamage = TRUE;
361 				while (mw->unmapCnt)
362 				{
363 				    unmapWindow (w);
364 				    mw->unmapCnt--;
365 				}
366 				mw->ignoreDamage = FALSE;
367 			    }
368 			}
369 		    }
370 		    else
371 		    {
372 			h = w->attrib.height + w->attrib.border_width * 2;
373 			if (mw->shade < h)
374 			{
375 			    mw->shade += (chunk * ms->shadeStep) + 1;
376 
377 			    if (mw->shade < h)
378 			    {
379 				ms->moreAdjust = TRUE;
380 			    }
381 			    else
382 			    {
383 				mw->shade = MAXSHORT;
384 
385 				minSetShade (w, h);
386 
387 				XDestroyRegion (mw->region);
388 				mw->region = NULL;
389 
390 				addWindowDamage (w);
391 			    }
392 			}
393 		    }
394 		}
395 	    }
396 
397 	    if (!ms->moreAdjust)
398 		break;
399 	}
400 
401 	if (ms->moreAdjust)
402 	{
403 	    for (w = s->windows; w; w = w->next)
404 	    {
405 		MIN_WINDOW (w);
406 
407 		if (mw->adjust)
408 		{
409 		    addWindowDamage (w);
410 		}
411 		else if (mw->region && w->damaged)
412 		{
413 		    h = w->attrib.height + w->attrib.border_width * 2;
414 		    if (mw->shade && mw->shade < h)
415 		    {
416 			minSetShade (w, mw->shade);
417 			addWindowDamage (w);
418 		    }
419 		}
420 	    }
421 	}
422     }
423 
424     UNWRAP (ms, s, preparePaintScreen);
425     (*s->preparePaintScreen) (s, msSinceLastPaint);
426     WRAP (ms, s, preparePaintScreen, minPreparePaintScreen);
427 }
428 
429 static void
minDonePaintScreen(CompScreen * s)430 minDonePaintScreen (CompScreen *s)
431 {
432     MIN_SCREEN (s);
433 
434     if (ms->moreAdjust)
435     {
436 	CompWindow *w;
437 	int	   h;
438 
439 	for (w = s->windows; w; w = w->next)
440 	{
441 	    MIN_WINDOW (w);
442 
443 	    if (mw->adjust)
444 	    {
445 		addWindowDamage (w);
446 	    }
447 	    else if (mw->region)
448 	    {
449 		h = w->attrib.height + w->attrib.border_width * 2;
450 		if (mw->shade && mw->shade < h)
451 		    addWindowDamage (w);
452 	    }
453 	}
454     }
455 
456     UNWRAP (ms, s, donePaintScreen);
457     (*s->donePaintScreen) (s);
458     WRAP (ms, s, donePaintScreen, minDonePaintScreen);
459 }
460 
461 static Bool
minPaintOutput(CompScreen * s,const ScreenPaintAttrib * sAttrib,const CompTransform * transform,Region region,CompOutput * output,unsigned int mask)462 minPaintOutput (CompScreen		*s,
463 		const ScreenPaintAttrib *sAttrib,
464 		const CompTransform	*transform,
465 		Region		        region,
466 		CompOutput		*output,
467 		unsigned int		mask)
468 {
469     Bool status;
470 
471     MIN_SCREEN (s);
472 
473     if (ms->moreAdjust)
474 	mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS_MASK;
475 
476     UNWRAP (ms, s, paintOutput);
477     status = (*s->paintOutput) (s, sAttrib, transform, region, output, mask);
478     WRAP (ms, s, paintOutput, minPaintOutput);
479 
480     return status;
481 }
482 
483 static Bool
minPaintWindow(CompWindow * w,const WindowPaintAttrib * attrib,const CompTransform * transform,Region region,unsigned int mask)484 minPaintWindow (CompWindow		*w,
485 		const WindowPaintAttrib *attrib,
486 		const CompTransform	*transform,
487 		Region			region,
488 		unsigned int		mask)
489 {
490     CompScreen *s = w->screen;
491     Bool       status;
492 
493     MIN_SCREEN (s);
494     MIN_WINDOW (w);
495 
496     if (mw->adjust)
497     {
498 	FragmentAttrib fragment;
499 	CompTransform  wTransform = *transform;
500 
501 	if (mask & PAINT_WINDOW_OCCLUSION_DETECTION_MASK)
502 	    return FALSE;
503 
504 	UNWRAP (ms, s, paintWindow);
505 	status = (*s->paintWindow) (w, attrib, transform, region,
506 				    mask | PAINT_WINDOW_NO_CORE_INSTANCE_MASK);
507 	WRAP (ms, s, paintWindow, minPaintWindow);
508 
509 	initFragmentAttrib (&fragment, &w->lastPaint);
510 
511 	if (w->alpha || fragment.opacity != OPAQUE)
512 	    mask |= PAINT_WINDOW_TRANSLUCENT_MASK;
513 
514 	matrixTranslate (&wTransform, w->attrib.x, w->attrib.y, 0.0f);
515 	matrixScale (&wTransform, mw->xScale, mw->yScale, 1.0f);
516 	matrixTranslate (&wTransform,
517 			 mw->tx / mw->xScale - w->attrib.x,
518 			 mw->ty / mw->yScale - w->attrib.y,
519 			 0.0f);
520 
521 	glPushMatrix ();
522 	glLoadMatrixf (wTransform.m);
523 
524 	(*s->drawWindow) (w, &wTransform, &fragment, region,
525 			  mask | PAINT_WINDOW_TRANSFORMED_MASK);
526 
527 	glPopMatrix ();
528     }
529     else
530     {
531 	/* no core instance from windows that have been animated */
532 	if (mw->state == IconicState)
533 	    mask |= PAINT_WINDOW_NO_CORE_INSTANCE_MASK;
534 
535 	UNWRAP (ms, s, paintWindow);
536 	status = (*s->paintWindow) (w, attrib, transform, region, mask);
537 	WRAP (ms, s, paintWindow, minPaintWindow);
538     }
539 
540     return status;
541 }
542 
543 static void
minHandleEvent(CompDisplay * d,XEvent * event)544 minHandleEvent (CompDisplay *d,
545 		XEvent      *event)
546 {
547     CompWindow *w;
548 
549     MIN_DISPLAY (d);
550 
551     switch (event->type) {
552     case MapNotify:
553 	w = findWindowAtDisplay (d, event->xmap.window);
554 	if (w)
555 	{
556 	    MIN_WINDOW (w);
557 
558 	    if (mw->adjust)
559 		mw->state = mw->newState;
560 
561 	    if (mw->region)
562 		w->height = 0;
563 
564 	    mw->ignoreDamage = TRUE;
565 	    while (mw->unmapCnt)
566 	    {
567 		unmapWindow (w);
568 		mw->unmapCnt--;
569 	    }
570 	    mw->ignoreDamage = FALSE;
571 	}
572 	break;
573     case UnmapNotify:
574 	w = findWindowAtDisplay (d, event->xunmap.window);
575 	if (w)
576 	{
577 	    MIN_SCREEN (w->screen);
578 
579 	    if (w->pendingUnmaps && onCurrentDesktop (w)) /* Normal -> Iconic */
580 	    {
581 		CompMatch *match =
582 		    &ms->opt[MIN_SCREEN_OPTION_WINDOW_MATCH].value.match;
583 
584 		MIN_WINDOW (w);
585 
586 		if (w->shaded)
587 		{
588 		    if (!mw->region)
589 			mw->region = XCreateRegion ();
590 
591 		    if (mw->region && ms->shadeStep)
592 		    {
593 			XSubtractRegion (w->region, &emptyRegion, mw->region);
594 			XOffsetRegion (mw->region, -w->attrib.x,
595 				       w->attrib.height +
596 				       w->attrib.border_width * 2 -
597 				       w->height - w->attrib.y);
598 
599 			mw->shade = w->height;
600 
601 			mw->adjust     = FALSE;
602 			ms->moreAdjust = TRUE;
603 
604 			mw->unmapCnt++;
605 			w->unmapRefCnt++;
606 
607 			addWindowDamage (w);
608 		    }
609 		}
610 		else if (!w->invisible && matchEval (match, w))
611 		{
612 		    if (w->iconGeometrySet)
613 		    {
614 			mw->newState = IconicState;
615 
616 			mw->xScale = w->paint.xScale;
617 			mw->yScale = w->paint.yScale;
618 			mw->tx	   = w->attrib.x - w->serverX;
619 			mw->ty	   = w->attrib.y - w->serverY;
620 
621 			if (mw->region)
622 			{
623 			    XDestroyRegion (mw->region);
624 			    mw->region = NULL;
625 			}
626 
627 			mw->shade = MAXSHORT;
628 
629 			mw->adjust     = TRUE;
630 			ms->moreAdjust = TRUE;
631 
632 			mw->unmapCnt++;
633 			w->unmapRefCnt++;
634 
635 			addWindowDamage (w);
636 		    }
637 		}
638 	    }
639 	    else  /* X -> Withdrawn */
640 	    {
641 		MIN_WINDOW (w);
642 
643 		if (mw->adjust)
644 		{
645 		    mw->adjust = FALSE;
646 		    mw->xScale = mw->yScale = 1.0f;
647 		    mw->tx = mw->ty = 0.0f;
648 		    mw->xVelocity = mw->yVelocity = 0.0f;
649 		    mw->xScaleVelocity = mw->yScaleVelocity = 1.0f;
650 		    mw->shade = MAXSHORT;
651 
652 		    if (mw->region)
653 		    {
654 			XDestroyRegion (mw->region);
655 			mw->region = NULL;
656 		    }
657 		}
658 
659 		mw->state = NormalState;
660 	    }
661 	}
662     default:
663 	break;
664     }
665 
666     UNWRAP (md, d, handleEvent);
667     (*d->handleEvent) (d, event);
668     WRAP (md, d, handleEvent, minHandleEvent);
669 }
670 
671 static Bool
minDamageWindowRect(CompWindow * w,Bool initial,BoxPtr rect)672 minDamageWindowRect (CompWindow *w,
673 		     Bool	initial,
674 		     BoxPtr     rect)
675 {
676     Bool status = FALSE;
677 
678     MIN_SCREEN (w->screen);
679     MIN_WINDOW (w);
680 
681     if (mw->ignoreDamage)
682 	return TRUE;
683 
684     if (initial)
685     {
686 	if (mw->state == IconicState)
687 	{
688 	    CompMatch *match =
689 		&ms->opt[MIN_SCREEN_OPTION_WINDOW_MATCH].value.match;
690 
691 	    mw->state = NormalState;
692 
693 	    if (!w->invisible	   &&
694 		w->iconGeometrySet &&
695 		matchEval (match, w))
696 	    {
697 		if (!mw->adjust)
698 		{
699 		    mw->adjust     = TRUE;
700 		    ms->moreAdjust = TRUE;
701 
702 		    mw->tx     = w->iconGeometry.x - w->serverX;
703 		    mw->ty     = w->iconGeometry.y - w->serverY;
704 		    mw->xScale = (float) w->iconGeometry.width  / w->width;
705 		    mw->yScale = (float) w->iconGeometry.height / w->height;
706 
707 		    addWindowDamage (w);
708 		}
709 	    }
710 	}
711 	else if (mw->region && mw->shade < w->height)
712 	{
713 	    if (ms->shadeStep && !w->invisible)
714 	    {
715 		XSubtractRegion (w->region, &emptyRegion, mw->region);
716 		XOffsetRegion (mw->region, -w->attrib.x, -w->attrib.y);
717 
718 		/* bind pixmap here so we have something to unshade with */
719 		if (!w->texture->pixmap && !w->bindFailed)
720 		    bindWindow (w);
721 
722 		ms->moreAdjust = TRUE;
723 	    }
724 	    else
725 	    {
726 		mw->shade = MAXSHORT;
727 	    }
728 	}
729 
730 	mw->newState = NormalState;
731     }
732     else if (mw->adjust)
733     {
734 	damageTransformedWindowRect (w,
735 				     mw->xScale,
736 				     mw->yScale,
737 				     mw->tx,
738 				     mw->ty,
739 				     rect);
740 
741 	status = TRUE;
742     }
743 
744     UNWRAP (ms, w->screen, damageWindowRect);
745     status |= (*w->screen->damageWindowRect) (w, initial, rect);
746     WRAP (ms, w->screen, damageWindowRect, minDamageWindowRect);
747 
748     return status;
749 }
750 
751 static Bool
minFocusWindow(CompWindow * w)752 minFocusWindow (CompWindow *w)
753 {
754     Bool status;
755 
756     MIN_SCREEN (w->screen);
757     MIN_WINDOW (w);
758 
759     if (mw->unmapCnt)
760 	return FALSE;
761 
762     UNWRAP (ms, w->screen, focusWindow);
763     status = (*w->screen->focusWindow) (w);
764     WRAP (ms, w->screen, focusWindow, minFocusWindow);
765 
766     return status;
767 }
768 
769 static Bool
minInitDisplay(CompPlugin * p,CompDisplay * d)770 minInitDisplay (CompPlugin  *p,
771 		CompDisplay *d)
772 {
773     MinDisplay *md;
774 
775     if (!checkPluginABI ("core", CORE_ABIVERSION))
776 	return FALSE;
777 
778     md = malloc (sizeof (MinDisplay));
779     if (!md)
780 	return FALSE;
781 
782     md->screenPrivateIndex = allocateScreenPrivateIndex (d);
783     if (md->screenPrivateIndex < 0)
784     {
785 	free (md);
786 	return FALSE;
787     }
788 
789     md->winChangeStateAtom  = XInternAtom (d->display, "WM_CHANGE_STATE", 0);
790 
791     WRAP (md, d, handleEvent, minHandleEvent);
792 
793     d->base.privates[displayPrivateIndex].ptr = md;
794 
795     return TRUE;
796 }
797 
798 static void
minFiniDisplay(CompPlugin * p,CompDisplay * d)799 minFiniDisplay (CompPlugin  *p,
800 		CompDisplay *d)
801 {
802     MIN_DISPLAY (d);
803 
804     freeScreenPrivateIndex (d, md->screenPrivateIndex);
805 
806     UNWRAP (md, d, handleEvent);
807 
808     free (md);
809 }
810 
811 static const CompMetadataOptionInfo minScreenOptionInfo[] = {
812     { "speed", "float", "<min>0.1</min>", 0, 0 },
813     { "timestep", "float", "<min>0.1</min>", 0, 0 },
814     { "window_match", "match", 0, 0, 0 },
815     { "shade_resistance", "int", "<min>0</min><max>100</max>", 0, 0 }
816 };
817 
818 static Bool
minInitScreen(CompPlugin * p,CompScreen * s)819 minInitScreen (CompPlugin *p,
820 	       CompScreen *s)
821 {
822     MinScreen *ms;
823 
824     MIN_DISPLAY (s->display);
825 
826     ms = malloc (sizeof (MinScreen));
827     if (!ms)
828 	return FALSE;
829 
830     if (!compInitScreenOptionsFromMetadata (s,
831 					    &minMetadata,
832 					    minScreenOptionInfo,
833 					    ms->opt,
834 					    MIN_SCREEN_OPTION_NUM))
835     {
836 	free (ms);
837 	return FALSE;
838     }
839 
840     ms->windowPrivateIndex = allocateWindowPrivateIndex (s);
841     if (ms->windowPrivateIndex < 0)
842     {
843 	compFiniScreenOptions (s, ms->opt, MIN_SCREEN_OPTION_NUM);
844 	free (ms);
845 	return FALSE;
846     }
847 
848     ms->moreAdjust = FALSE;
849     ms->shadeStep  = ms->opt[MIN_SCREEN_OPTION_SHADE_RESISTANCE].rest.i.max -
850 	ms->opt[MIN_SCREEN_OPTION_SHADE_RESISTANCE].value.i + 1;
851 
852     WRAP (ms, s, preparePaintScreen, minPreparePaintScreen);
853     WRAP (ms, s, donePaintScreen, minDonePaintScreen);
854     WRAP (ms, s, paintOutput, minPaintOutput);
855     WRAP (ms, s, paintWindow, minPaintWindow);
856     WRAP (ms, s, damageWindowRect, minDamageWindowRect);
857     WRAP (ms, s, focusWindow, minFocusWindow);
858 
859     s->base.privates[md->screenPrivateIndex].ptr = ms;
860 
861     return TRUE;
862 }
863 
864 static void
minFiniScreen(CompPlugin * p,CompScreen * s)865 minFiniScreen (CompPlugin *p,
866 	       CompScreen *s)
867 {
868     MIN_SCREEN (s);
869 
870     freeWindowPrivateIndex (s, ms->windowPrivateIndex);
871 
872     UNWRAP (ms, s, preparePaintScreen);
873     UNWRAP (ms, s, donePaintScreen);
874     UNWRAP (ms, s, paintOutput);
875     UNWRAP (ms, s, paintWindow);
876     UNWRAP (ms, s, damageWindowRect);
877     UNWRAP (ms, s, focusWindow);
878 
879     compFiniScreenOptions (s, ms->opt, MIN_SCREEN_OPTION_NUM);
880 
881     free (ms);
882 }
883 
884 static Bool
minInitWindow(CompPlugin * p,CompWindow * w)885 minInitWindow (CompPlugin *p,
886 	       CompWindow *w)
887 {
888     MinWindow *mw;
889 
890     MIN_SCREEN (w->screen);
891 
892     mw = malloc (sizeof (MinWindow));
893     if (!mw)
894 	return FALSE;
895 
896     mw->xScale = mw->yScale = 1.0f;
897     mw->tx = mw->ty = 0.0f;
898     mw->adjust = FALSE;
899     mw->xVelocity = mw->yVelocity = 0.0f;
900     mw->xScaleVelocity = mw->yScaleVelocity = 1.0f;
901 
902     mw->unmapCnt = 0;
903 
904     mw->ignoreDamage = FALSE;
905 
906     if (w->state & CompWindowStateHiddenMask)
907     {
908 	if (w->shaded)
909 	{
910 	    mw->state = mw->newState = NormalState;
911 	    mw->shade = 0;
912 	    mw->region = XCreateRegion ();
913 	}
914 	else
915 	{
916 	    mw->state = mw->newState = minGetWindowState (w);
917 	    mw->shade = MAXSHORT;
918 	    mw->region = NULL;
919 	}
920     }
921     else
922     {
923 	    mw->state = mw->newState = NormalState;
924 	    mw->shade = MAXSHORT;
925 	    mw->region = NULL;
926     }
927 
928     w->base.privates[ms->windowPrivateIndex].ptr = mw;
929 
930     return TRUE;
931 }
932 
933 static void
minFiniWindow(CompPlugin * p,CompWindow * w)934 minFiniWindow (CompPlugin *p,
935 	       CompWindow *w)
936 {
937     MIN_WINDOW (w);
938 
939     mw->ignoreDamage = TRUE;
940     while (mw->unmapCnt--)
941 	unmapWindow (w);
942     mw->ignoreDamage = FALSE;
943 
944     if (mw->region)
945 	XDestroyRegion (mw->region);
946 
947     free (mw);
948 }
949 
950 static CompBool
minInitObject(CompPlugin * p,CompObject * o)951 minInitObject (CompPlugin *p,
952 	       CompObject *o)
953 {
954     static InitPluginObjectProc dispTab[] = {
955 	(InitPluginObjectProc) 0, /* InitCore */
956 	(InitPluginObjectProc) minInitDisplay,
957 	(InitPluginObjectProc) minInitScreen,
958 	(InitPluginObjectProc) minInitWindow
959     };
960 
961     RETURN_DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), TRUE, (p, o));
962 }
963 
964 static void
minFiniObject(CompPlugin * p,CompObject * o)965 minFiniObject (CompPlugin *p,
966 	       CompObject *o)
967 {
968     static FiniPluginObjectProc dispTab[] = {
969 	(FiniPluginObjectProc) 0, /* FiniCore */
970 	(FiniPluginObjectProc) minFiniDisplay,
971 	(FiniPluginObjectProc) minFiniScreen,
972 	(FiniPluginObjectProc) minFiniWindow
973     };
974 
975     DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), (p, o));
976 }
977 
978 static CompOption *
minGetObjectOptions(CompPlugin * plugin,CompObject * object,int * count)979 minGetObjectOptions (CompPlugin *plugin,
980 		     CompObject *object,
981 		     int	*count)
982 {
983     static GetPluginObjectOptionsProc dispTab[] = {
984 	(GetPluginObjectOptionsProc) 0, /* GetCoreOptions */
985 	(GetPluginObjectOptionsProc) 0, /* GetDisplayOptions */
986 	(GetPluginObjectOptionsProc) minGetScreenOptions
987     };
988 
989     *count = 0;
990     RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab),
991 		     (void *) count, (plugin, object, count));
992 }
993 
994 static CompBool
minSetObjectOption(CompPlugin * plugin,CompObject * object,const char * name,CompOptionValue * value)995 minSetObjectOption (CompPlugin      *plugin,
996 		    CompObject      *object,
997 		    const char      *name,
998 		    CompOptionValue *value)
999 {
1000     static SetPluginObjectOptionProc dispTab[] = {
1001 	(SetPluginObjectOptionProc) 0, /* SetCoreOption */
1002 	(SetPluginObjectOptionProc) 0, /* SetDisplayOption */
1003 	(SetPluginObjectOptionProc) minSetScreenOption
1004     };
1005 
1006     RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab), FALSE,
1007 		     (plugin, object, name, value));
1008 }
1009 
1010 static Bool
minInit(CompPlugin * p)1011 minInit (CompPlugin *p)
1012 {
1013     if (!compInitPluginMetadataFromInfo (&minMetadata,
1014 					 p->vTable->name, 0, 0,
1015 					 minScreenOptionInfo,
1016 					 MIN_SCREEN_OPTION_NUM))
1017 	return FALSE;
1018 
1019     displayPrivateIndex = allocateDisplayPrivateIndex ();
1020     if (displayPrivateIndex < 0)
1021     {
1022 	compFiniMetadata (&minMetadata);
1023 	return FALSE;
1024     }
1025 
1026     compAddMetadataFromFile (&minMetadata, p->vTable->name);
1027 
1028     return TRUE;
1029 }
1030 
1031 static void
minFini(CompPlugin * p)1032 minFini (CompPlugin *p)
1033 {
1034     freeDisplayPrivateIndex (displayPrivateIndex);
1035     compFiniMetadata (&minMetadata);
1036 }
1037 
1038 static CompMetadata *
minGetMetadata(CompPlugin * plugin)1039 minGetMetadata (CompPlugin *plugin)
1040 {
1041     return &minMetadata;
1042 }
1043 
1044 static CompPluginVTable minVTable = {
1045     "minimize",
1046     minGetMetadata,
1047     minInit,
1048     minFini,
1049     minInitObject,
1050     minFiniObject,
1051     minGetObjectOptions,
1052     minSetObjectOption
1053 };
1054 
1055 CompPluginVTable *
getCompPluginInfo20070830(void)1056 getCompPluginInfo20070830 (void)
1057 {
1058     return &minVTable;
1059 }
1060