1 /*
2  * Compiz splash plugin
3  *
4  * splash.c
5  *
6  * Copyright : (C) 2006 by Dennis Kasprzyk
7  * E-mail    : onestone@beryl-project.org
8  *
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  */
21 
22 #include <math.h>
23 
24 #include <compiz-core.h>
25 #include <X11/Xatom.h>
26 
27 #include "splash_options.h"
28 
29 #define SPLASH_BACKGROUND_DEFAULT ""
30 #define SPLASH_LOGO_DEFAULT ""
31 
32 #define GET_SPLASH_DISPLAY(d)                                  \
33     ((SplashDisplay *) (d)->base.privates[displayPrivateIndex].ptr)
34 
35 #define SPLASH_DISPLAY(d)                      \
36     SplashDisplay *sd = GET_SPLASH_DISPLAY (d)
37 
38 #define GET_SPLASH_SCREEN(s, sd)                                   \
39     ((SplashScreen *) (s)->base.privates[(sd)->screenPrivateIndex].ptr)
40 
41 #define SPLASH_SCREEN(s)                                                      \
42     SplashScreen *ss = GET_SPLASH_SCREEN (s, GET_SPLASH_DISPLAY (s->display))
43 
44 
45 static int displayPrivateIndex = 0;
46 
47 typedef struct _SplashDisplay
48 {
49     Atom splashAtom;
50     int screenPrivateIndex;
51 }
52 SplashDisplay;
53 
54 #define MESH_W 16
55 #define MESH_H 16
56 
57 typedef struct _SplashScreen
58 {
59     PreparePaintScreenProc preparePaintScreen;
60     DonePaintScreenProc    donePaintScreen;
61     PaintOutputProc        paintOutput;
62     PaintWindowProc        paintWindow;
63 
64     int fade_in;
65     int fade_out;
66     int time;
67 
68     CompTexture back_img, logo_img;
69     unsigned int backSize[2], logoSize[2];
70     Bool hasInit, hasLogo, hasBack;
71 
72     float mesh[MESH_W][MESH_H][2];
73     float mMove;
74 
75     float brightness;
76     float saturation;
77 
78     Bool initiate;
79     Bool active;
80 
81 }
82 SplashScreen;
83 
84 
85 static void
splashPreparePaintScreen(CompScreen * s,int ms)86 splashPreparePaintScreen (CompScreen *s,
87 			  int        ms)
88 {
89     SPLASH_SCREEN (s);
90     CompDisplay *d = s->display;
91 
92     Bool lastShot = FALSE;
93 
94     ss->fade_in -= ms;
95 
96     if (ss->fade_in < 0)
97     {
98 	ss->time += ss->fade_in;
99 	ss->fade_in = 0;
100 
101 	if (ss->time < 0)
102 	{
103 	    if (ss->fade_out > 0 && ss->fade_out <= ms)
104 		lastShot = TRUE;
105 
106 	    ss->fade_out += ss->time;
107 
108 	    ss->time = 0;
109 
110 	    if (ss->fade_out < 0)
111 		ss->fade_out = 0;
112 	}
113     }
114 
115     if (ss->initiate)
116     {
117 	ss->fade_in = ss->fade_out = splashGetFadeTime (d) * 1000.0;
118 	ss->time = splashGetDisplayTime (d) * 1000.0;
119 	ss->initiate = FALSE;
120     }
121 
122     if (ss->fade_in || ss->fade_out || ss->time || lastShot)
123     {
124 	ss->active = TRUE;
125 	ss->mMove += ms / 500.0;
126 
127 	if (!ss->hasInit)
128 	{
129 	    ss->hasInit = TRUE;
130 	    ss->mMove = 0.0;
131 
132 	    ss->hasBack =
133 		readImageToTexture (s, &ss->back_img, splashGetBackground (d),
134 				    &ss->backSize[0], &ss->backSize[1]);
135 	    ss->hasLogo =
136 		readImageToTexture (s, &ss->logo_img, splashGetLogo (d),
137 				    &ss->logoSize[0], &ss->logoSize[1]);
138 
139 	    if (!ss->hasBack)
140 	    {
141 		ss->hasBack =
142 		    readImageToTexture (s, &ss->back_img,
143 					SPLASH_BACKGROUND_DEFAULT,
144 					&ss->backSize[0], &ss->backSize[1]);
145 
146 		if (ss->hasBack)
147 		{
148 		    compLogMessage ("splash", CompLogLevelWarn,
149 				    "Could not load splash background image "
150 				    "\"%s\" using default!",
151 				    splashGetBackground (d) );
152 		}
153 	    }
154 
155 	    if (!ss->hasLogo)
156 	    {
157 		ss->hasLogo =
158 		    readImageToTexture (s, &ss->logo_img,
159 					SPLASH_LOGO_DEFAULT,
160 					&ss->logoSize[0], &ss->logoSize[1]);
161 
162 		if (ss->hasLogo)
163 		{
164 		    compLogMessage ("splash", CompLogLevelWarn,
165 				    "Could not load splash logo image "
166 				    "\"%s\" using default!",
167 				    splashGetLogo (d) );
168 		}
169 	    }
170 
171 	    if (!ss->hasBack)
172 		compLogMessage ("splash", CompLogLevelWarn,
173 				"Could not load splash background image "
174 				"\"%s\" !", splashGetBackground (d) );
175 
176 	    if (!ss->hasLogo)
177 		compLogMessage ("splash", CompLogLevelWarn,
178 				"Could not load splash logo image \"%s\" !",
179 				splashGetLogo (d) );
180 	}
181     }
182     else
183     {
184 	ss->active = FALSE;
185 
186 	if (ss->hasInit)
187 	{
188 	    ss->hasInit = FALSE;
189 
190 	    if (ss->hasBack)
191 	    {
192 		finiTexture (s, &ss->back_img);
193 		initTexture (s, &ss->back_img);
194 		ss->hasBack = FALSE;
195 	    }
196 
197 	    if (ss->hasLogo)
198 	    {
199 		finiTexture (s, &ss->logo_img);
200 		initTexture (s, &ss->logo_img);
201 		ss->hasLogo = FALSE;
202 	    }
203 	}
204     }
205 
206     UNWRAP (ss, s, preparePaintScreen);
207     (*s->preparePaintScreen) (s, ms);
208     WRAP (ss, s, preparePaintScreen, splashPreparePaintScreen);
209 
210 }
211 
212 static void
splashDonePaintScreen(CompScreen * s)213 splashDonePaintScreen (CompScreen * s)
214 {
215     SPLASH_SCREEN (s);
216 
217     if (ss->fade_in || ss->fade_out || ss->time)
218 	damageScreen (s);
219 
220     UNWRAP (ss, s, donePaintScreen);
221     (*s->donePaintScreen) (s);
222     WRAP (ss, s, donePaintScreen, splashDonePaintScreen);
223 }
224 
225 static void
splashGetCurrentOutputRect(CompScreen * s,XRectangle * outputRect)226 splashGetCurrentOutputRect (CompScreen *s,
227 			    XRectangle *outputRect)
228 {
229     int root_x = 0, root_y = 0;
230     int ignore_i;
231     unsigned int ignore_ui;
232     int output;
233     Window ignore_w;
234 
235 
236     if (s->nOutputDev == 1)
237 	output = 0;
238     else
239     {
240 	XQueryPointer (s->display->display, s->root, &ignore_w, &ignore_w,
241 		       &root_x, &root_y, &ignore_i, &ignore_i, &ignore_ui);
242 	output = outputDeviceForPoint (s, root_x, root_y);
243     }
244 
245     outputRect->x      = s->outputDev[output].region.extents.x1;
246     outputRect->y      = s->outputDev[output].region.extents.y1;
247     outputRect->width  = s->outputDev[output].region.extents.x2 -
248 			 s->outputDev[output].region.extents.x1;
249     outputRect->height = s->outputDev[output].region.extents.y2 -
250 			 s->outputDev[output].region.extents.y1;
251 
252 }
253 
254 static Bool
splashPaintOutput(CompScreen * s,const ScreenPaintAttrib * sa,const CompTransform * transform,Region region,CompOutput * output,unsigned int mask)255 splashPaintOutput (CompScreen              *s,
256 		   const ScreenPaintAttrib *sa,
257 		   const CompTransform     *transform,
258 		   Region                  region,
259 		   CompOutput              *output,
260 		   unsigned int            mask)
261 {
262 
263     SPLASH_SCREEN (s);
264     CompDisplay *d = s->display;
265     CompTransform sTransform = *transform;
266 
267     Bool status = TRUE;
268 
269     float alpha = 0.0;
270 
271     if (ss->active)
272     {
273 	alpha = (1.0 - (ss->fade_in / (splashGetFadeTime (d) * 1000.0) ) ) *
274 		(ss->fade_out / (splashGetFadeTime (d) * 1000.0) );
275 	ss->saturation = 1.0 -
276 			 ((1.0 - (splashGetSaturation (d) / 100.0) ) * alpha);
277 	ss->brightness = 1.0 -
278 			 ((1.0 - (splashGetBrightness (d) / 100.0) ) * alpha);
279     }
280 
281     UNWRAP (ss, s, paintOutput);
282     status = (*s->paintOutput) (s, sa, transform, region, output, mask);
283     WRAP (ss, s, paintOutput, splashPaintOutput);
284 
285     if (!ss->active)
286 	return status;
287 
288 
289     transformToScreenSpace (s, output, -DEFAULT_Z_CAMERA, &sTransform);
290 
291     glPushMatrix ();
292     glLoadMatrixf (sTransform.m);
293     glEnable (GL_BLEND);
294     glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
295     glColor4f (1.0, 1.0, 1.0, alpha);
296     glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
297 
298     if (ss->hasBack)
299     {
300 	int x, y;
301 
302 	for (x = 0; x < MESH_W; x++)
303 	{
304 	    for (y = 0; y < MESH_H; y++)
305 	    {
306 		ss->mesh[x][y][0] =
307 		    (x / (MESH_W - 1.0) ) +
308 		    (0.02 * sin ( (y / (MESH_H - 1.0) * 8) + ss->mMove) );
309 		ss->mesh[x][y][1] =
310 		    (y / (MESH_H - 1.0) ) +
311 		    (0.02 * sin ( (ss->mesh[x][y][0] * 8) + ss->mMove) );
312 		;
313 	    }
314 	}
315 
316 	enableTexture (s, &ss->back_img, COMP_TEXTURE_FILTER_GOOD);
317 
318 	if (s->nOutputDev > 1)
319 	{
320 	    XRectangle headOutputRect;
321 
322 	    splashGetCurrentOutputRect (s, &headOutputRect);
323 
324 	    x = (headOutputRect.width - ss->backSize[0]) / 2;
325 	    y = (headOutputRect.height - ss->backSize[1]) / 2;
326 
327 	    x += headOutputRect.x;
328 	    y += headOutputRect.y;
329 	}
330 	else
331 	{
332 	    x = (s->width - ss->backSize[0]) / 2;
333 	    y = (s->height - ss->backSize[1]) / 2;
334 	}
335 
336 	CompMatrix mat = ss->back_img.matrix;
337 
338 	glTranslatef (x, y, 0);
339 
340 	float cx1, cx2, cy1, cy2;
341 
342 	glBegin (GL_QUADS);
343 
344 	for (x = 0; x < MESH_W - 1; x++)
345 	{
346 	    for (y = 0; y < MESH_H - 1; y++)
347 	    {
348 		cx1 = (x / (MESH_W - 1.0) ) * ss->backSize[0];
349 		cx2 = ( (x + 1) / (MESH_W - 1.0) ) * ss->backSize[0];
350 		cy1 = (y / (MESH_H - 1.0) ) * ss->backSize[1];
351 		cy2 = ( (y + 1) / (MESH_H - 1.0) ) * ss->backSize[1];
352 
353 		glTexCoord2f (COMP_TEX_COORD_X (&mat, cx1),
354 			      COMP_TEX_COORD_Y (&mat, cy1) );
355 		glVertex2f (ss->mesh[x][y][0] *
356 			    ss->backSize[0],
357 			    ss->mesh[x][y][1] * ss->backSize[1]);
358 		glTexCoord2f (COMP_TEX_COORD_X (&mat, cx1),
359 			      COMP_TEX_COORD_Y (&mat, cy2) );
360 		glVertex2f (ss->mesh[x][y + 1][0] *
361 			    ss->backSize[0],
362 			    ss->mesh[x][y + 1][1] * ss->backSize[1]);
363 		glTexCoord2f (COMP_TEX_COORD_X (&mat, cx2),
364 			      COMP_TEX_COORD_Y (&mat, cy2) );
365 		glVertex2f (ss->mesh[x + 1][y + 1][0] *
366 			    ss->backSize[0],
367 			    ss->mesh[x + 1][y + 1][1] * ss->backSize[1]);
368 		glTexCoord2f (COMP_TEX_COORD_X (&mat, cx2),
369 			      COMP_TEX_COORD_Y (&mat, cy1) );
370 		glVertex2f (ss->mesh[x + 1][y][0] *
371 			    ss->backSize[0],
372 			    ss->mesh[x + 1][y][1] * ss->backSize[1]);
373 	    }
374 	}
375 
376 	glEnd ();
377 
378 	if (s->nOutputDev > 1)
379 	{
380 	    XRectangle headOutputRect;
381 
382 	    splashGetCurrentOutputRect (s, &headOutputRect);
383 
384 	    x = (headOutputRect.width - ss->backSize[0]) / 2;
385 	    y = (headOutputRect.height - ss->backSize[1]) / 2;
386 
387 	    x += headOutputRect.x;
388 	    y += headOutputRect.y;
389 	}
390 	else
391 	{
392 	    x = (s->width - ss->backSize[0]) / 2;
393 	    y = (s->height - ss->backSize[1]) / 2;
394 	}
395 
396 	glTranslatef (-x, -y, 0);
397 
398 	disableTexture (s, &ss->back_img);
399     }
400 
401     if (ss->hasLogo)
402     {
403 	enableTexture (s, &ss->logo_img, COMP_TEXTURE_FILTER_GOOD);
404 	int x, y;
405 
406 	if (s->nOutputDev > 1)
407 	{
408 	    XRectangle headOutputRect;
409 
410 	    splashGetCurrentOutputRect (s, &headOutputRect);
411 	    x = (headOutputRect.width - ss->logoSize[0]) / 2;
412 	    y = (headOutputRect.height - ss->logoSize[1]) / 2;
413 
414 	    x += headOutputRect.x;
415 	    y += headOutputRect.y;
416 	}
417 	else
418 	{
419 	    x = (s->width - ss->logoSize[0]) / 2;
420 	    y = (s->height - ss->logoSize[1]) / 2;
421 	}
422 
423 	CompMatrix mat = ss->logo_img.matrix;
424 
425 	glTranslatef (x, y, 0);
426 
427 	glBegin (GL_QUADS);
428 	glTexCoord2f (COMP_TEX_COORD_X (&mat, 0), COMP_TEX_COORD_Y (&mat, 0) );
429 	glVertex2f (0, 0);
430 	glTexCoord2f (COMP_TEX_COORD_X (&mat, 0),
431 		      COMP_TEX_COORD_Y (&mat, ss->logoSize[1]) );
432 	glVertex2f (0, ss->logoSize[1]);
433 	glTexCoord2f (COMP_TEX_COORD_X (&mat, ss->logoSize[0]),
434 		      COMP_TEX_COORD_Y (&mat, ss->logoSize[1]) );
435 	glVertex2f (ss->logoSize[0], ss->logoSize[1]);
436 	glTexCoord2f (COMP_TEX_COORD_X (&mat, ss->logoSize[0]),
437 		      COMP_TEX_COORD_Y (&mat, 0) );
438 	glVertex2f (ss->logoSize[0], 0);
439 	glEnd ();
440 
441 	glTranslatef (-x, -y, 0);
442 
443 	disableTexture (s, &ss->logo_img);
444     }
445 
446     glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
447 
448     glDisable (GL_BLEND);
449     glBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
450     glColor4usv (defaultColor);
451     glPopMatrix ();
452     return status;
453 }
454 
455 static Bool
splashPaintWindow(CompWindow * w,const WindowPaintAttrib * attrib,const CompTransform * transform,Region region,unsigned int mask)456 splashPaintWindow (CompWindow              *w,
457 		   const WindowPaintAttrib *attrib,
458 		   const CompTransform     *transform,
459 		   Region                  region,
460 		   unsigned int            mask)
461 {
462     CompScreen *s = w->screen;
463     Bool status;
464 
465     SPLASH_SCREEN (s);
466 
467     if (ss->active)
468     {
469 	WindowPaintAttrib pA = *attrib;
470 	pA.brightness = attrib->brightness * ss->brightness;
471 	pA.saturation = attrib->saturation * ss->saturation;
472 
473 	UNWRAP (ss, s, paintWindow);
474 	status = (*s->paintWindow) (w, &pA, transform, region, mask);
475 	WRAP (ss, s, paintWindow, splashPaintWindow);
476     }
477     else
478     {
479 	UNWRAP (ss, s, paintWindow);
480 	status = (*s->paintWindow) (w, attrib, transform, region, mask);
481 	WRAP (ss, s, paintWindow, splashPaintWindow);
482     }
483 
484     return status;
485 }
486 
487 static Bool
splashInitScreen(CompPlugin * p,CompScreen * s)488 splashInitScreen (CompPlugin *p,
489 		  CompScreen *s)
490 {
491 
492     SPLASH_DISPLAY (s->display);
493 
494     SplashScreen *ss = (SplashScreen *) calloc (1, sizeof (SplashScreen) );
495 
496     s->base.privates[sd->screenPrivateIndex].ptr = ss;
497 
498     WRAP (ss, s, paintOutput, splashPaintOutput);
499     WRAP (ss, s, preparePaintScreen, splashPreparePaintScreen);
500     WRAP (ss, s, donePaintScreen, splashDonePaintScreen);
501     WRAP (ss, s, paintWindow, splashPaintWindow);
502 
503     initTexture (s, &ss->back_img);
504     initTexture (s, &ss->logo_img);
505 
506     ss->initiate = FALSE;
507 
508     if (splashGetFirststart (s->display) )
509     {
510 	Atom actual;
511 	int result, format;
512 	unsigned long n, left;
513 	unsigned char *propData;
514 
515 	result = XGetWindowProperty (s->display->display, s->root,
516 				     sd->splashAtom, 0L, 8192L, FALSE,
517 				     XA_INTEGER, &actual, &format,
518 				     &n, &left, &propData);
519 
520 	if (result == Success && n && propData)
521 	{
522 	    XFree (propData);
523 	}
524 	else
525 	{
526 	    int value = 1;
527 	    XChangeProperty (s->display->display, s->root, sd->splashAtom,
528 			     XA_INTEGER, 32, PropModeReplace,
529 			     (unsigned char *) &value, 1);
530 	    ss->initiate = TRUE;
531 	}
532     }
533 
534     return TRUE;
535 }
536 
537 
538 static void
splashFiniScreen(CompPlugin * p,CompScreen * s)539 splashFiniScreen (CompPlugin *p,
540 		  CompScreen *s)
541 {
542 
543     SPLASH_SCREEN (s);
544 
545     //Restore the original function
546     UNWRAP (ss, s, paintOutput);
547     UNWRAP (ss, s, preparePaintScreen);
548     UNWRAP (ss, s, donePaintScreen);
549     UNWRAP (ss, s, paintWindow);
550 
551     finiTexture (s, &ss->back_img);
552     finiTexture (s, &ss->logo_img);
553 
554     //Free the pointer
555     free (ss);
556 }
557 
558 
559 
560 static Bool
splashInitiate(CompDisplay * d,CompAction * ac,CompActionState state,CompOption * option,int nOption)561 splashInitiate (CompDisplay     *d,
562 		CompAction      *ac,
563 		CompActionState state,
564 		CompOption      *option,
565 		int             nOption)
566 {
567     CompScreen *s;
568 
569     s = findScreenAtDisplay (d, getIntOptionNamed (option, nOption, "root", 0));
570 
571     if (s)
572     {
573 	SPLASH_SCREEN (s);
574 	ss->initiate = TRUE;
575 	damageScreen (s);
576     }
577 
578     return FALSE;
579 }
580 
581 
582 static Bool
splashInitDisplay(CompPlugin * p,CompDisplay * d)583 splashInitDisplay (CompPlugin  *p,
584 		   CompDisplay *d)
585 {
586     SplashDisplay *sd;
587 
588     if (!checkPluginABI ("core", CORE_ABIVERSION))
589 	return FALSE;
590 
591     /* Generate a splash display */
592     sd = (SplashDisplay *) malloc (sizeof (SplashDisplay) );
593 
594     if (!sd)
595 	return FALSE;
596 
597     /* Allocate a private index */
598     sd->screenPrivateIndex = allocateScreenPrivateIndex (d);
599 
600     /* Check if its valid */
601     if (sd->screenPrivateIndex < 0)
602     {
603 	/* It's invalid so free memory and return */
604 	free (sd);
605 	return FALSE;
606     }
607 
608     sd->splashAtom = XInternAtom (d->display, "_COMPIZ_WM_SPLASH", 0);
609 
610     splashSetInitiateKeyInitiate (d, splashInitiate);
611 
612     d->base.privates[displayPrivateIndex].ptr = sd;
613     return TRUE;
614 }
615 
616 static void
splashFiniDisplay(CompPlugin * p,CompDisplay * d)617 splashFiniDisplay (CompPlugin  *p,
618 		   CompDisplay *d)
619 {
620     SPLASH_DISPLAY (d);
621 
622     /* Free the private index */
623     freeScreenPrivateIndex (d, sd->screenPrivateIndex);
624 
625     /* Free the pointer */
626     free (sd);
627 }
628 
629 static Bool
splashInit(CompPlugin * p)630 splashInit (CompPlugin *p)
631 {
632     displayPrivateIndex = allocateDisplayPrivateIndex ();
633 
634     if (displayPrivateIndex < 0)
635 	return FALSE;
636 
637     return TRUE;
638 }
639 
640 static void
splashFini(CompPlugin * p)641 splashFini (CompPlugin *p)
642 {
643     if (displayPrivateIndex >= 0)
644 	freeDisplayPrivateIndex (displayPrivateIndex);
645 }
646 
647 static CompBool
splashInitObject(CompPlugin * p,CompObject * o)648 splashInitObject (CompPlugin *p,
649 		  CompObject *o)
650 {
651     static InitPluginObjectProc dispTab[] = {
652 	(InitPluginObjectProc) 0, /* InitCore */
653 	(InitPluginObjectProc) splashInitDisplay,
654 	(InitPluginObjectProc) splashInitScreen
655     };
656 
657     RETURN_DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), TRUE, (p, o));
658 }
659 
660 static void
splashFiniObject(CompPlugin * p,CompObject * o)661 splashFiniObject (CompPlugin *p,
662 		  CompObject *o)
663 {
664     static FiniPluginObjectProc dispTab[] = {
665 	(FiniPluginObjectProc) 0, /* FiniCore */
666 	(FiniPluginObjectProc) splashFiniDisplay,
667 	(FiniPluginObjectProc) splashFiniScreen
668     };
669 
670     DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), (p, o));
671 }
672 
673 CompPluginVTable splashVTable = {
674     "splash",
675     0,
676     splashInit,
677     splashFini,
678     splashInitObject,
679     splashFiniObject,
680     0,
681     0
682 };
683 
684 CompPluginVTable *
getCompPluginInfo(void)685 getCompPluginInfo (void)
686 {
687     return &splashVTable;
688 }
689