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 #ifdef HAVE_CONFIG_H
27 #  include "../config.h"
28 #endif
29 
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <signal.h>
33 #include <unistd.h>
34 #include <string.h>
35 #include <sys/wait.h>
36 
37 #include <compiz-core.h>
38 
39 char *programName;
40 char **programArgv;
41 int  programArgc;
42 
43 char **initialPlugins = NULL;
44 int  nInitialPlugins = 0;
45 
46 char *backgroundImage = NULL;
47 
48 REGION   emptyRegion;
49 REGION   infiniteRegion;
50 GLushort defaultColor[4] = { 0xffff, 0xffff, 0xffff, 0xffff };
51 Window   currentRoot = 0;
52 
53 int  defaultRefreshRate = 50;
54 char *defaultTextureFilter = "Good";
55 
56 Bool shutDown = FALSE;
57 Bool restartSignal = FALSE;
58 Bool coreInitialized = FALSE;
59 
60 CompWindow *lastFoundWindow = 0;
61 CompWindow *lastDamagedWindow = 0;
62 
63 Bool replaceCurrentWm = FALSE;
64 Bool indirectRendering = FALSE;
65 Bool strictBinding = TRUE;
66 Bool noDetection = FALSE;
67 Bool useDesktopHints = FALSE;
68 Bool onlyCurrentScreen = FALSE;
69 static Bool debugOutput = FALSE;
70 
71 #ifdef USE_COW
72 Bool useCow = TRUE;
73 #endif
74 
75 CompMetadata coreMetadata;
76 
77 static void
usage(void)78 usage (void)
79 {
80     printf ("Usage: %s\n       "
81 	    "[--display DISPLAY] "
82 	    "[--bg-image PNG] "
83 	    "[--refresh-rate RATE]\n       "
84 	    "[--fast-filter] "
85 	    "[--indirect-rendering] "
86 	    "[--no-detection]\n       "
87 	    "[--keep-desktop-hints] "
88 	    "[--loose-binding] "
89 	    "[--replace]\n       "
90 	    "[--sm-disable] "
91 	    "[--sm-client-id ID] "
92 	    "[--only-current-screen]\n      "
93 
94 #ifdef USE_COW
95 	    " [--use-root-window] "
96 #endif
97 
98 	    "[--debug] "
99 	    "[--version] "
100 	    "[--help] "
101 	    "[PLUGIN]...\n",
102 	    programName);
103 }
104 
105 void
compLogMessage(const char * componentName,CompLogLevel level,const char * format,...)106 compLogMessage (const char   *componentName,
107 		CompLogLevel level,
108 		const char   *format,
109 		...)
110 {
111     va_list args;
112     char    message[2048];
113 
114     va_start (args, format);
115 
116     vsnprintf (message, 2048, format, args);
117 
118     if (coreInitialized)
119 	(*core.logMessage) (componentName, level, message);
120     else
121 	logMessage (componentName, level, message);
122 
123     va_end (args);
124 }
125 
126 void
logMessage(const char * componentName,CompLogLevel level,const char * message)127 logMessage (const char	 *componentName,
128 	    CompLogLevel level,
129 	    const char	 *message)
130 {
131     if (!debugOutput && level >= CompLogLevelDebug)
132 	return;
133 
134     fprintf (stderr, "%s (%s) - %s: %s\n",
135 	      programName, componentName,
136 	      logLevelToString (level), message);
137 }
138 
139 const char *
logLevelToString(CompLogLevel level)140 logLevelToString (CompLogLevel level)
141 {
142     switch (level) {
143     case CompLogLevelFatal:
144 	return "Fatal";
145     case CompLogLevelError:
146 	return "Error";
147     case CompLogLevelWarn:
148 	return "Warn";
149     case CompLogLevelInfo:
150 	return "Info";
151     case CompLogLevelDebug:
152 	return "Debug";
153     default:
154 	break;
155     }
156 
157     return "Unknown";
158 }
159 
160 static void
signalHandler(int sig)161 signalHandler (int sig)
162 {
163     int status;
164 
165     switch (sig) {
166     case SIGCHLD:
167 	waitpid (-1, &status, WNOHANG | WUNTRACED);
168 	break;
169     case SIGHUP:
170 	restartSignal = TRUE;
171 	break;
172     case SIGINT:
173     case SIGTERM:
174 	shutDown = TRUE;
175     default:
176 	break;
177     }
178 }
179 
180 typedef struct _CompIOCtx {
181     int	 offset;
182     char *pluginData;
183     char *textureFilterData;
184     char *refreshRateData;
185 } CompIOCtx;
186 
187 static int
readCoreXmlCallback(void * context,char * buffer,int length)188 readCoreXmlCallback (void *context,
189 		     char *buffer,
190 		     int  length)
191 {
192     CompIOCtx *ctx = (CompIOCtx *) context;
193     int	      offset = ctx->offset;
194     int	      i, j;
195 
196     i = compReadXmlChunk ("<compiz><core><display>", &offset, buffer, length);
197 
198     for (j = 0; j < COMP_DISPLAY_OPTION_NUM; j++)
199     {
200 	CompMetadataOptionInfo info = coreDisplayOptionInfo[j];
201 
202 	switch (j) {
203 	case COMP_DISPLAY_OPTION_ACTIVE_PLUGINS:
204 	    if (ctx->pluginData)
205 		info.data = ctx->pluginData;
206 	    break;
207 	case COMP_DISPLAY_OPTION_TEXTURE_FILTER:
208 	    if (ctx->textureFilterData)
209 		info.data = ctx->textureFilterData;
210 	default:
211 	    break;
212 	}
213 
214 	i += compReadXmlChunkFromMetadataOptionInfo (&info,
215 						     &offset,
216 						     buffer + i,
217 						     length - i);
218     }
219 
220     i += compReadXmlChunk ("</display><screen>", &offset,
221 			   buffer + i, length - 1);
222 
223     for (j = 0; j < COMP_SCREEN_OPTION_NUM; j++)
224     {
225 	CompMetadataOptionInfo info = coreScreenOptionInfo[j];
226 
227 	switch (j) {
228 	case COMP_SCREEN_OPTION_REFRESH_RATE:
229 	    if (ctx->refreshRateData)
230 		info.data = ctx->refreshRateData;
231 	default:
232 	    break;
233 	}
234 
235 	i += compReadXmlChunkFromMetadataOptionInfo (&info,
236 						     &offset,
237 						     buffer + i,
238 						     length - i);
239     }
240 
241     i += compReadXmlChunk ("</screen></core></compiz>", &offset, buffer + i,
242 			   length - i);
243 
244     if (!offset && length > i)
245 	buffer[i++] = '\0';
246 
247     ctx->offset += i;
248 
249     return i;
250 }
251 
252 int
main(int argc,char ** argv)253 main (int argc, char **argv)
254 {
255     CompIOCtx ctx;
256     char      *displayName = 0;
257     char      *plugin[256];
258     int	      i, nPlugin = 0;
259     Bool      disableSm = FALSE;
260     char      *clientId = NULL;
261     char      *refreshRateArg = NULL;
262 
263     programName = argv[0];
264     programArgc = argc;
265     programArgv = argv;
266 
267     signal (SIGHUP, signalHandler);
268     signal (SIGCHLD, signalHandler);
269     signal (SIGINT, signalHandler);
270     signal (SIGTERM, signalHandler);
271 
272     emptyRegion.rects = &emptyRegion.extents;
273     emptyRegion.numRects = 0;
274     emptyRegion.extents.x1 = 0;
275     emptyRegion.extents.y1 = 0;
276     emptyRegion.extents.x2 = 0;
277     emptyRegion.extents.y2 = 0;
278     emptyRegion.size = 0;
279 
280     infiniteRegion.rects = &infiniteRegion.extents;
281     infiniteRegion.numRects = 1;
282     infiniteRegion.extents.x1 = MINSHORT;
283     infiniteRegion.extents.y1 = MINSHORT;
284     infiniteRegion.extents.x2 = MAXSHORT;
285     infiniteRegion.extents.y2 = MAXSHORT;
286 
287     memset (&ctx, 0, sizeof (ctx));
288 
289     for (i = 1; i < argc; i++)
290     {
291 	if (!strcmp (argv[i], "--help"))
292 	{
293 	    usage ();
294 	    return 0;
295 	}
296 	else if (!strcmp (argv[i], "--version"))
297 	{
298 	    printf (PACKAGE_STRING "\n");
299 	    return 0;
300 	}
301 	else if (!strcmp (argv[i], "--debug"))
302 	{
303 	    debugOutput = TRUE;
304 	}
305 	else if (!strcmp (argv[i], "--display"))
306 	{
307 	    if (i + 1 < argc)
308 		displayName = argv[++i];
309 	}
310 	else if (!strcmp (argv[i], "--refresh-rate"))
311 	{
312 	    if (i + 1 < argc)
313 	    {
314 		refreshRateArg = programArgv[++i];
315 		defaultRefreshRate = atoi (refreshRateArg);
316 		defaultRefreshRate = RESTRICT_VALUE (defaultRefreshRate,
317 						     1, 1000);
318 	    }
319 	}
320 	else if (!strcmp (argv[i], "--fast-filter"))
321 	{
322 	    ctx.textureFilterData = "<default>Fast</default>";
323 	    defaultTextureFilter = "Fast";
324 	}
325 	else if (!strcmp (argv[i], "--indirect-rendering"))
326 	{
327 	    /* force Mesa libGL into indirect rendering mode, because
328 	       glXQueryExtensionsString is context-independant */
329 	    setenv ("LIBGL_ALWAYS_INDIRECT", "1", True);
330 	    indirectRendering = TRUE;
331 	}
332 	else if (!strcmp (argv[i], "--loose-binding"))
333 	{
334 	    strictBinding = FALSE;
335 	}
336 	else if (!strcmp (argv[i], "--ignore-desktop-hints"))
337 	{
338 	    /* keep command line parameter for backward compatibility */
339 	    useDesktopHints = FALSE;
340 	}
341 	else if (!strcmp (argv[i], "--keep-desktop-hints"))
342 	{
343 	    useDesktopHints = TRUE;
344 	}
345 	else if (!strcmp (argv[i], "--only-current-screen"))
346 	{
347 	    onlyCurrentScreen = TRUE;
348 	}
349 
350 #ifdef USE_COW
351 	else if (!strcmp (argv[i], "--use-root-window"))
352 	{
353 	    useCow = FALSE;
354 	}
355 #endif
356 
357 	else if (!strcmp (argv[i], "--replace"))
358 	{
359 	    replaceCurrentWm = TRUE;
360 	}
361 	else if (!strcmp (argv[i], "--sm-disable"))
362 	{
363 	    disableSm = TRUE;
364 	}
365 	else if (!strcmp (argv[i], "--sm-client-id"))
366 	{
367 	    if (i + 1 < argc)
368 		clientId = argv[++i];
369 	}
370 	else if (!strcmp (argv[i], "--no-detection"))
371 	{
372 	    noDetection = TRUE;
373 	}
374 	else if (!strcmp (argv[i], "--bg-image"))
375 	{
376 	    if (i + 1 < argc)
377 		backgroundImage = argv[++i];
378 	}
379 	else if (*argv[i] == '-')
380 	{
381 	    compLogMessage ("core", CompLogLevelWarn,
382 			    "Unknown option '%s'\n", argv[i]);
383 	}
384 	else
385 	{
386 	    if (nPlugin < 256)
387 		plugin[nPlugin++] = argv[i];
388 	}
389     }
390 
391     if (refreshRateArg)
392     {
393 	ctx.refreshRateData = malloc (strlen (refreshRateArg) + 256);
394 	if (ctx.refreshRateData)
395 	    sprintf (ctx.refreshRateData,
396 		     "<min>1</min><default>%s</default>",
397 		     refreshRateArg);
398     }
399 
400     if (nPlugin)
401     {
402 	int size = 256;
403 
404 	for (i = 0; i < nPlugin; i++)
405 	    size += strlen (plugin[i]) + 16;
406 
407 	ctx.pluginData = malloc (size);
408 	if (ctx.pluginData)
409 	{
410 	    char *ptr = ctx.pluginData;
411 
412 	    ptr += sprintf (ptr, "<type>string</type><default>");
413 
414 	    for (i = 0; i < nPlugin; i++)
415 		ptr += sprintf (ptr, "<value>%s</value>", plugin[i]);
416 
417 	    ptr += sprintf (ptr, "</default>");
418 	}
419 
420 	initialPlugins = malloc (nPlugin * sizeof (char *));
421 	if (initialPlugins)
422 	{
423 	    memcpy (initialPlugins, plugin, nPlugin * sizeof (char *));
424 	    nInitialPlugins = nPlugin;
425 	}
426 	else
427 	{
428 	    nInitialPlugins = 0;
429 	}
430     }
431 
432     xmlInitParser ();
433 
434     LIBXML_TEST_VERSION;
435 
436     if (!compInitMetadata (&coreMetadata))
437     {
438 	compLogMessage ("core", CompLogLevelFatal,
439 			"Couldn't initialize core metadata");
440 	return 1;
441     }
442 
443     if (!compAddMetadataFromIO (&coreMetadata,
444 				readCoreXmlCallback, NULL,
445 				&ctx))
446 	return 1;
447 
448     if (ctx.refreshRateData)
449 	free (ctx.refreshRateData);
450 
451     if (ctx.pluginData)
452 	free (ctx.pluginData);
453 
454     compAddMetadataFromFile (&coreMetadata, "core");
455 
456     if (!initCore ())
457 	return 1;
458 
459     coreInitialized = TRUE;
460 
461     if (!disableSm)
462 	initSession (clientId);
463 
464     if (!addDisplay (displayName))
465 	return 1;
466 
467     eventLoop ();
468 
469     if (!disableSm)
470 	closeSession ();
471 
472     coreInitialized = FALSE;
473 
474     finiCore ();
475     compFiniMetadata (&coreMetadata);
476 
477     xmlCleanupParser ();
478 
479     if (initialPlugins)
480         free (initialPlugins);
481 
482     if (restartSignal)
483     {
484 	execvp (programName, programArgv);
485 	return 1;
486     }
487 
488     return 0;
489 }
490