1 /*
2 * Compiz configuration system library
3 *
4 * Copyright (C) 2007 Dennis Kasprzyk <onestone@opencompositing.org>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 #ifdef USE_PROTOBUF
22 #include "compizconfig.pb.h"
23 #include <google/protobuf/io/zero_copy_stream_impl.h>
24 #endif
25
26 extern "C"
27 {
28 #ifdef HAVE_CONFIG_H
29 # include "../config.h"
30 #endif
31
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <dirent.h>
36 #include <sys/stat.h>
37 #include <errno.h>
38
39 #include <libxslt/transform.h>
40 #include <libxslt/xsltutils.h>
41
42 #include <locale.h>
43
44 #include <compiz-core.h>
45 #include <ccs.h>
46 #include "ccs-private.h"
47 }
48
49 extern int xmlLoadExtDtdDefaultValue;
50
51 static const char *
getLocale()52 getLocale ()
53 {
54 char *lang = getenv ("LC_ALL");
55
56 if (!lang || !strlen (lang))
57 lang = getenv ("LC_MESSAGES");
58
59 if (!lang || !strlen (lang))
60 lang = getenv ("LANG");
61
62 return lang ? lang : "";
63 }
64
65 #ifdef USE_PROTOBUF
66
67 Bool usingProtobuf = TRUE;
68
69 #define PB_ABI_VERSION 20090314
70
71 typedef metadata::PluginInfo PluginInfoMetadata;
72 typedef metadata::PluginBrief PluginBriefMetadata;
73 typedef metadata::Plugin PluginMetadata;
74
75 typedef PluginInfoMetadata::Dependencies DependenciesMetadata;
76 typedef PluginMetadata::Screen ScreenMetadata;
77 typedef PluginMetadata::Option OptionMetadata;
78 typedef PluginMetadata::Extension ExtensionMetadata;
79 typedef OptionMetadata::GenericValue GenericValueMetadata;
80
81 typedef google::protobuf::RepeatedPtrField< std::string > StringList;
82
83 PluginBriefMetadata persistentPluginBriefPB;
84 PluginMetadata persistentPluginPB; // Made global so that it gets reused,
85 // for better performance (to avoid
86 // mem alloc/free for each plugin)
87
88 std::string metadataCacheDir = "";
89
90 std::string curLocale = std::string (getLocale ());
91 std::string shortLocale = curLocale.find ('.') == std::string::npos ?
92 curLocale : curLocale.substr (0, curLocale.find ('.'));
93
94 #endif
95
96 static void
ccsAddRestrictionToStringInfo(CCSSettingStringInfo * forString,const char * name,const char * value)97 ccsAddRestrictionToStringInfo (CCSSettingStringInfo *forString,
98 const char *name,
99 const char *value)
100 {
101 CCSStrRestriction *restriction;
102
103 restriction = (CCSStrRestriction *) calloc (1, sizeof (CCSStrRestriction));
104 if (restriction)
105 {
106 restriction->name = strdup (name);
107 restriction->value = strdup (value);
108 forString->restriction =
109 ccsStrRestrictionListAppend (forString->restriction,
110 restriction);
111 }
112 }
113
114 static void
ccsAddRestrictionToStringExtension(CCSStrExtension * extension,const char * name,const char * value)115 ccsAddRestrictionToStringExtension (CCSStrExtension *extension,
116 const char *name,
117 const char *value)
118 {
119 CCSStrRestriction *restriction;
120
121 restriction = (CCSStrRestriction *) calloc (1, sizeof (CCSStrRestriction));
122 if (restriction)
123 {
124 restriction->name = strdup (name);
125 restriction->value = strdup (value);
126 extension->restriction =
127 ccsStrRestrictionListAppend (extension->restriction, restriction);
128 }
129 }
130
131
132 #ifdef USE_PROTOBUF
133
134 static void
initBoolValuePB(CCSSettingValue * v,const GenericValueMetadata & value)135 initBoolValuePB (CCSSettingValue * v,
136 const GenericValueMetadata & value)
137 {
138 v->value.asBool = FALSE;
139
140 if (value.has_bool_value ())
141 {
142 v->value.asBool = value.bool_value ();
143 }
144 }
145
146 static void
initIntValuePB(CCSSettingValue * v,CCSSettingInfo * i,const GenericValueMetadata & value)147 initIntValuePB (CCSSettingValue * v,
148 CCSSettingInfo * i,
149 const GenericValueMetadata & value)
150 {
151 v->value.asInt = (i->forInt.min + i->forInt.max) / 2;
152
153 if (value.has_int_value ())
154 {
155 int val = value.int_value ();
156 if (val >= i->forInt.min && val <= i->forInt.max)
157 v->value.asInt = val;
158 }
159 }
160
161 static void
initFloatValuePB(CCSSettingValue * v,CCSSettingInfo * i,const GenericValueMetadata & value)162 initFloatValuePB (CCSSettingValue * v,
163 CCSSettingInfo * i,
164 const GenericValueMetadata & value)
165 {
166 v->value.asFloat = (i->forFloat.min + i->forFloat.max) / 2;
167
168 if (value.has_float_value ())
169 {
170 float val = value.float_value ();
171 if (val >= i->forFloat.min && val <= i->forFloat.max)
172 v->value.asFloat = val;
173 }
174 }
175
176 static void
initStringValuePB(CCSSettingValue * v,CCSSettingInfo * i,const GenericValueMetadata & value)177 initStringValuePB (CCSSettingValue * v,
178 CCSSettingInfo * i,
179 const GenericValueMetadata & value)
180 {
181 free (v->value.asString);
182
183 if (value.has_string_value ())
184 v->value.asString = strdup (value.string_value ().c_str ());
185 else
186 v->value.asString = strdup ("");
187 }
188
189 static void
initColorValuePB(CCSSettingValue * v,const GenericValueMetadata & value)190 initColorValuePB (CCSSettingValue * v,
191 const GenericValueMetadata & value)
192 {
193 memset (&v->value.asColor, 0, sizeof (v->value.asColor));
194 v->value.asColor.color.alpha = 0xffff;
195
196 if (!value.has_color_value ())
197 return;
198
199 const OptionMetadata::ColorValue &val = value.color_value ();
200
201 if (val.has_red ())
202 {
203 int color = strtol ((char *) val.red ().c_str (), NULL, 0);
204
205 v->value.asColor.color.red = MAX (0, MIN (0xffff, color));
206 }
207
208 if (val.has_green ())
209 {
210 int color = strtol ((char *) val.green ().c_str (), NULL, 0);
211
212 v->value.asColor.color.green = MAX (0, MIN (0xffff, color));
213 }
214
215 if (val.has_blue ())
216 {
217 int color = strtol ((char *) val.blue ().c_str (), NULL, 0);
218
219 v->value.asColor.color.blue = MAX (0, MIN (0xffff, color));
220 }
221
222 if (val.has_alpha ())
223 {
224 int color = strtol ((char *) val.alpha ().c_str (), NULL, 0);
225
226 v->value.asColor.color.alpha = MAX (0, MIN (0xffff, color));
227 }
228 }
229
230 static void
initMatchValuePB(CCSSettingValue * v,const GenericValueMetadata & value)231 initMatchValuePB (CCSSettingValue * v,
232 const GenericValueMetadata & value)
233 {
234 free (v->value.asMatch);
235
236 if (value.has_string_value ())
237 v->value.asMatch = strdup (value.string_value ().c_str ());
238 else
239 v->value.asMatch = strdup ("");
240 }
241
242 static void
initKeyValuePB(CCSSettingValue * v,CCSSettingInfo * i,const GenericValueMetadata & value)243 initKeyValuePB (CCSSettingValue * v,
244 CCSSettingInfo * i,
245 const GenericValueMetadata & value)
246 {
247 memset (&v->value.asKey, 0, sizeof (v->value.asKey));
248
249 if (value.has_string_value ())
250 {
251 const char * val = value.string_value ().c_str ();
252
253 if (strcasecmp (val, "disabled"))
254 {
255 ccsStringToKeyBinding (val, &v->value.asKey);
256 }
257 }
258 }
259
260 static void
initButtonValuePB(CCSSettingValue * v,CCSSettingInfo * i,const GenericValueMetadata & value)261 initButtonValuePB (CCSSettingValue * v,
262 CCSSettingInfo * i,
263 const GenericValueMetadata & value)
264 {
265 memset (&v->value.asButton, 0, sizeof (v->value.asButton));
266
267 if (value.has_string_value ())
268 {
269 const char * val = value.string_value ().c_str ();
270
271 if (strcasecmp (val, "disabled"))
272 {
273 ccsStringToButtonBinding (val, &v->value.asButton);
274 }
275 }
276 }
277
278 static void
initEdgeValuePB(CCSSettingValue * v,CCSSettingInfo * i,const GenericValueMetadata & value)279 initEdgeValuePB (CCSSettingValue * v,
280 CCSSettingInfo * i,
281 const GenericValueMetadata & value)
282 {
283 v->value.asEdge = 0;
284
285 if (value.has_edge_value ())
286 v->value.asEdge = value.edge_value ();
287 }
288
289 static void
initBellValuePB(CCSSettingValue * v,CCSSettingInfo * i,const GenericValueMetadata & value)290 initBellValuePB (CCSSettingValue * v,
291 CCSSettingInfo * i,
292 const GenericValueMetadata & value)
293 {
294 v->value.asBell = FALSE;
295
296 if (value.has_bool_value ())
297 v->value.asBell = value.bool_value ();
298 }
299
300 static void
initListValuePB(CCSSettingValue * v,CCSSettingInfo * i,const OptionMetadata & option)301 initListValuePB (CCSSettingValue * v,
302 CCSSettingInfo * i,
303 const OptionMetadata & option)
304 {
305 int num, j;
306
307 num = option.default_value_size ();
308
309 if (num)
310 {
311 for (j = 0; j < num; j++)
312 {
313 CCSSettingValue *val;
314 val = (CCSSettingValue *) calloc (1, sizeof (CCSSettingValue));
315 if (!val)
316 continue;
317
318 val->parent = v->parent;
319 val->isListChild = TRUE;
320
321 switch (i->forList.listType)
322 {
323 case TypeBool:
324 initBoolValuePB (val, option.default_value (j));
325 break;
326 case TypeInt:
327 initIntValuePB (val, i->forList.listInfo,
328 option.default_value (j));
329 break;
330 case TypeFloat:
331 initFloatValuePB (val, i->forList.listInfo,
332 option.default_value (j));
333 break;
334 case TypeString:
335 initStringValuePB (val, i->forList.listInfo,
336 option.default_value (j));
337 break;
338 case TypeColor:
339 initColorValuePB (val, option.default_value (j));
340 break;
341 case TypeKey:
342 initKeyValuePB (val, i->forList.listInfo,
343 option.default_value (j));
344 break;
345 case TypeButton:
346 initButtonValuePB (val, i->forList.listInfo,
347 option.default_value (j));
348 break;
349 case TypeEdge:
350 initEdgeValuePB (val, i->forList.listInfo,
351 option.default_value (j));
352 break;
353 case TypeBell:
354 initBellValuePB (val, i->forList.listInfo,
355 option.default_value (j));
356 break;
357 case TypeMatch:
358 initMatchValuePB (val, option.default_value (j));
359 default:
360 break;
361 }
362 v->value.asList = ccsSettingValueListAppend (v->value.asList, val);
363 }
364 }
365 }
366
367 static void
initIntInfoPB(CCSSettingInfo * i,const OptionMetadata & option)368 initIntInfoPB (CCSSettingInfo * i, const OptionMetadata & option)
369 {
370 int num, j;
371 i->forInt.min = MINSHORT;
372 i->forInt.max = MAXSHORT;
373 i->forInt.desc = NULL;
374
375 if (option.has_int_min ())
376 i->forInt.min = option.int_min ();
377
378 if (option.has_int_max ())
379 i->forInt.max = option.int_max ();
380
381 if (!basicMetadata)
382 {
383 num = option.int_desc_size ();
384 for (j = 0; j < num; j++)
385 {
386 const OptionMetadata::IntDescription & intDescMetadata =
387 option.int_desc (j);
388
389 int val = intDescMetadata.value ();
390
391 if (val >= i->forInt.min && val <= i->forInt.max)
392 {
393 CCSIntDesc *intDesc;
394
395 intDesc = (CCSIntDesc *) calloc (1, sizeof (CCSIntDesc));
396 if (intDesc)
397 {
398 intDesc->name = strdup (intDescMetadata.name ().c_str ());
399 intDesc->value = val;
400 i->forInt.desc =
401 ccsIntDescListAppend (i->forInt.desc, intDesc);
402 }
403 }
404 }
405 }
406 }
407
408 static void
initFloatInfoPB(CCSSettingInfo * i,const OptionMetadata & option)409 initFloatInfoPB (CCSSettingInfo * i, const OptionMetadata & option)
410 {
411 i->forFloat.min = MINSHORT;
412 i->forFloat.max = MAXSHORT;
413 i->forFloat.precision = 0.1f;
414
415 if (option.has_float_min ())
416 i->forFloat.min = option.float_min ();
417
418 if (option.has_float_max ())
419 i->forFloat.max = option.float_max ();
420
421 if (option.precision ())
422 i->forFloat.precision = option.precision ();
423 }
424
425 static void
initStringInfoPB(CCSSettingInfo * i,const OptionMetadata & option)426 initStringInfoPB (CCSSettingInfo * i, const OptionMetadata & option)
427 {
428 int num, j;
429 i->forString.restriction = NULL;
430 i->forString.sortStartsAt = -1;
431 i->forString.extensible = FALSE;
432
433 if (!basicMetadata)
434 {
435 if (option.has_extensible () && option.extensible ())
436 i->forString.extensible = TRUE;
437
438 if (option.has_sort_start ())
439 i->forString.sortStartsAt = option.sort_start ();
440
441 num = option.str_restriction_size ();
442 for (j = 0; j < num; j++)
443 {
444 const OptionMetadata::StringRestriction &
445 restrictionMetadata = option.str_restriction (j);
446
447 const char *value = restrictionMetadata.value ().c_str ();
448 const char *name = restrictionMetadata.name ().c_str ();
449
450 ccsAddRestrictionToStringInfo (&i->forString, value, name);
451 }
452 }
453 }
454
455 static void
initListInfoPB(CCSSettingInfo * i,const OptionMetadata & option)456 initListInfoPB (CCSSettingInfo * i, const OptionMetadata & option)
457 {
458 CCSSettingInfo *info;
459
460 i->forList.listType = TypeBool;
461 i->forList.listInfo = NULL;
462
463 if (option.has_list_type ())
464 {
465 i->forList.listType = (CCSSettingType) option.list_type ();
466 }
467 switch (i->forList.listType)
468 {
469 case TypeInt:
470 {
471 info = (CCSSettingInfo *) calloc (1, sizeof (CCSSettingInfo));
472 if (info)
473 initIntInfoPB (info, option);
474 i->forList.listInfo = info;
475 }
476 break;
477 case TypeFloat:
478 {
479 info = (CCSSettingInfo *) calloc (1, sizeof (CCSSettingInfo));
480 if (info)
481 initFloatInfoPB (info, option);
482 i->forList.listInfo = info;
483 }
484 break;
485 case TypeString:
486 {
487 info = (CCSSettingInfo *) calloc (1, sizeof (CCSSettingInfo));
488 if (info)
489 initStringInfoPB (info, option);
490 i->forList.listInfo = info;
491 }
492 break;
493 default:
494 break;
495 }
496 }
497
498 static void
initActionInfoPB(CCSSettingInfo * i,const OptionMetadata & option)499 initActionInfoPB (CCSSettingInfo * i, const OptionMetadata & option)
500 {
501 i->forAction.internal = FALSE;
502
503 if (option.has_internal () && option.internal ())
504 i->forAction.internal = TRUE;
505 }
506
507 static void
addOptionForPluginPB(CCSPlugin * plugin,const char * name,Bool isScreen,unsigned int screen,const StringList & groups,const StringList & subgroups,const OptionMetadata & option)508 addOptionForPluginPB (CCSPlugin * plugin,
509 const char * name,
510 Bool isScreen,
511 unsigned int screen,
512 const StringList & groups,
513 const StringList & subgroups,
514 const OptionMetadata & option)
515 {
516 CCSSetting *setting;
517
518 if (ccsFindSetting (plugin, name, isScreen, screen))
519 {
520 fprintf (stderr, "[ERROR]: Option \"%s\" already defined\n", name);
521 return;
522 }
523
524 setting = (CCSSetting *) calloc (1, sizeof (CCSSetting));
525 if (!setting)
526 return;
527
528 setting->parent = plugin;
529 setting->isScreen = isScreen;
530 setting->screenNum = screen;
531 setting->isDefault = TRUE;
532 setting->name = strdup (name);
533
534 if (!basicMetadata)
535 {
536 setting->shortDesc =
537 strdup (option.has_short_desc () ?
538 option.short_desc ().c_str () :
539 name);
540 setting->longDesc =
541 strdup (option.has_long_desc () ?
542 option.long_desc ().c_str () :
543 name);
544 setting->hints = strdup (option.has_hints () ?
545 option.hints ().c_str () :
546 name);
547 setting->group =
548 strdup (option.group_id () >= 0 ?
549 groups.Get (option.group_id ()).c_str () :
550 "");
551 setting->subGroup =
552 strdup (option.subgroup_id () >= 0 ?
553 subgroups.Get (option.subgroup_id ()).c_str () :
554 "");
555 }
556 else
557 {
558 setting->shortDesc = strdup (name);
559 setting->longDesc = strdup ("");
560 setting->hints = strdup ("");
561 setting->group = strdup ("");
562 setting->subGroup = strdup ("");
563 }
564
565 setting->type = (CCSSettingType) option.type ();
566 setting->value = &setting->defaultValue;
567 setting->defaultValue.parent = setting;
568
569 switch (setting->type)
570 {
571 case TypeInt:
572 initIntInfoPB (&setting->info, option);
573 break;
574 case TypeFloat:
575 initFloatInfoPB (&setting->info, option);
576 break;
577 case TypeString:
578 initStringInfoPB (&setting->info, option);
579 break;
580 case TypeList:
581 initListInfoPB (&setting->info, option);
582 break;
583 case TypeKey:
584 case TypeButton:
585 case TypeEdge:
586 case TypeBell:
587 initActionInfoPB (&setting->info, option);
588 break;
589 case TypeAction: // do nothing and fall through
590 default:
591 break;
592 }
593
594 if (option.default_value_size () > 0)
595 {
596 switch (setting->type)
597 {
598 case TypeInt:
599 initIntValuePB (&setting->defaultValue, &setting->info,
600 option.default_value (0));
601 break;
602 case TypeBool:
603 initBoolValuePB (&setting->defaultValue, option.default_value (0));
604 break;
605 case TypeFloat:
606 initFloatValuePB (&setting->defaultValue, &setting->info,
607 option.default_value (0));
608 break;
609 case TypeString:
610 initStringValuePB (&setting->defaultValue, &setting->info,
611 option.default_value (0));
612 break;
613 case TypeColor:
614 initColorValuePB (&setting->defaultValue, option.default_value (0));
615 break;
616 case TypeKey:
617 initKeyValuePB (&setting->defaultValue, &setting->info,
618 option.default_value (0));
619 break;
620 case TypeButton:
621 initButtonValuePB (&setting->defaultValue, &setting->info,
622 option.default_value (0));
623 break;
624 case TypeEdge:
625 initEdgeValuePB (&setting->defaultValue, &setting->info,
626 option.default_value (0));
627 break;
628 case TypeBell:
629 initBellValuePB (&setting->defaultValue, &setting->info,
630 option.default_value (0));
631 break;
632 case TypeMatch:
633 initMatchValuePB (&setting->defaultValue,
634 option.default_value (0));
635 break;
636 case TypeList:
637 initListValuePB (&setting->defaultValue, &setting->info,
638 option);
639 break;
640 case TypeAction: // do nothing and fall through
641 default:
642 break;
643 }
644 }
645 else
646 {
647 /* if we have no set defaults, we have at least to set
648 the string defaults to empty strings */
649 switch (setting->type)
650 {
651 case TypeString:
652 setting->defaultValue.value.asString = strdup ("");
653 break;
654 case TypeMatch:
655 setting->defaultValue.value.asMatch = strdup ("");
656 break;
657 default:
658 break;
659 }
660 }
661
662 PLUGIN_PRIV (plugin);
663
664 pPrivate->settings = ccsSettingListAppend (pPrivate->settings, setting);
665 }
666
667 static void
addOptionFromPB(CCSPlugin * plugin,Bool isScreen,const StringList & groups,const StringList & subgroups,const OptionMetadata & option)668 addOptionFromPB (CCSPlugin * plugin,
669 Bool isScreen,
670 const StringList & groups,
671 const StringList & subgroups,
672 const OptionMetadata & option)
673 {
674 const char *name;
675 Bool readonly = FALSE;
676
677 name = option.name ().c_str ();
678
679 readonly = option.has_read_only () && option.read_only ();
680
681 if (!strlen (name) || readonly)
682 return;
683
684 if (isScreen)
685 {
686 for (unsigned i = 0; i < plugin->context->numScreens; i++)
687 addOptionForPluginPB (plugin, name, TRUE,
688 plugin->context->screens[i],
689 groups, subgroups, option);
690 }
691 else
692 addOptionForPluginPB (plugin, name, FALSE, 0, groups, subgroups, option);
693 }
694
695 static void
initOptionsFromPB(CCSPlugin * plugin,const PluginMetadata & pluginPB)696 initOptionsFromPB (CCSPlugin * plugin,
697 const PluginMetadata & pluginPB)
698 {
699 int numOpt, i;
700
701 if (pluginPB.has_display ())
702 {
703 const ScreenMetadata &displayPB = pluginPB.display ();
704
705 // Display options
706 numOpt = displayPB.option_size ();
707 for (i = 0; i < numOpt; i++)
708 addOptionFromPB (plugin, FALSE,
709 displayPB.group_desc (),
710 displayPB.subgroup_desc (),
711 displayPB.option (i));
712 }
713
714 if (pluginPB.has_screen ())
715 {
716 const ScreenMetadata &screenPB = pluginPB.screen ();
717
718 // Screen options
719 numOpt = screenPB.option_size ();
720 for (i = 0; i < numOpt; i++)
721 addOptionFromPB (plugin, TRUE,
722 screenPB.group_desc (),
723 screenPB.subgroup_desc (),
724 screenPB.option (i));
725 }
726 }
727
728 static void
addStringsFromPB(CCSStringList * list,const StringList & strings)729 addStringsFromPB (CCSStringList * list,
730 const StringList & strings)
731 {
732 StringList::const_iterator it;
733
734 for (it = strings.begin (); it != strings.end (); it++)
735 {
736 const char *value = (*it).c_str ();
737
738 if (strlen (value))
739 *list = ccsStringListAppend (*list, strdup (value));
740 }
741 }
742
743 static void
addStringExtensionFromPB(CCSPlugin * plugin,const ExtensionMetadata & extensionPB)744 addStringExtensionFromPB (CCSPlugin * plugin,
745 const ExtensionMetadata & extensionPB)
746 {
747 int j;
748 CCSStrExtension *extension;
749
750 extension = (CCSStrExtension *) calloc (1, sizeof (CCSStrExtension));
751 if (!extension)
752 return;
753
754 extension->isScreen = !(extensionPB.has_display () &&
755 extensionPB.display ());
756
757 extension->restriction = NULL;
758
759 extension->basePlugin = strdup (extensionPB.base_plugin ().c_str ());
760
761 addStringsFromPB (&extension->baseSettings,
762 extensionPB.base_option ());
763
764 int numRestrictions = extensionPB.str_restriction_size ();
765 if (!numRestrictions)
766 {
767 free (extension);
768 return;
769 }
770
771 for (j = 0; j < numRestrictions; j++)
772 {
773 const OptionMetadata::StringRestriction & restrictionPB =
774 extensionPB.str_restriction (j);
775
776 const char *value = restrictionPB.value ().c_str ();
777 const char *name = restrictionPB.name ().c_str ();
778
779 ccsAddRestrictionToStringExtension (extension, name, value);
780 }
781
782 PLUGIN_PRIV (plugin);
783
784 pPrivate->stringExtensions =
785 ccsStrExtensionListAppend (pPrivate->stringExtensions, extension);
786 }
787
788 static void
initStringExtensionsFromPB(CCSPlugin * plugin,const PluginMetadata & pluginPB)789 initStringExtensionsFromPB (CCSPlugin * plugin,
790 const PluginMetadata & pluginPB)
791 {
792 int numExtensions, i;
793
794 numExtensions = pluginPB.extension_size ();
795 for (i = 0; i < numExtensions; i++)
796 addStringExtensionFromPB (plugin, pluginPB.extension (i));
797 }
798
799 static void
initRulesFromPB(CCSPlugin * plugin,const PluginInfoMetadata & pluginInfoPB)800 initRulesFromPB (CCSPlugin * plugin, const PluginInfoMetadata & pluginInfoPB)
801 {
802 addStringsFromPB (&plugin->providesFeature, pluginInfoPB.feature ());
803
804 if (!pluginInfoPB.has_deps ())
805 return;
806
807 const DependenciesMetadata & deps = pluginInfoPB.deps ();
808
809 addStringsFromPB (&plugin->loadAfter, deps.after_plugin ());
810 addStringsFromPB (&plugin->loadBefore, deps.before_plugin ());
811 addStringsFromPB (&plugin->requiresPlugin, deps.require_plugin ());
812 addStringsFromPB (&plugin->requiresFeature, deps.require_feature ());
813 addStringsFromPB (&plugin->conflictPlugin, deps.conflict_plugin ());
814 addStringsFromPB (&plugin->conflictFeature, deps.conflict_feature ());
815 }
816
817 static void
addPluginFromPB(CCSContext * context,const PluginInfoMetadata & pluginInfoPB,char * file,char * xmlFile)818 addPluginFromPB (CCSContext * context,
819 const PluginInfoMetadata & pluginInfoPB,
820 char *file,
821 char *xmlFile)
822 {
823 const char *name;
824 CCSPlugin *plugin;
825 CCSPluginPrivate *pPrivate;
826
827 name = pluginInfoPB.name ().c_str ();
828
829 if (!strlen (name))
830 return;
831
832 if (ccsFindPlugin (context, name))
833 return;
834
835 if (!strcmp (name, "ini") || !strcmp (name, "gconf") ||
836 !strcmp (name, "ccp") || !strcmp (name, "kconfig"))
837 return;
838
839 plugin = (CCSPlugin *) calloc (1, sizeof (CCSPlugin));
840 if (!plugin)
841 return;
842
843 pPrivate = (CCSPluginPrivate *) calloc (1, sizeof (CCSPluginPrivate));
844 if (!pPrivate)
845 {
846 free (plugin);
847 return;
848 }
849 pPrivate->loaded = FALSE;
850
851 plugin->ccsPrivate = (void *) pPrivate;
852
853 if (file)
854 pPrivate->pbFilePath = strdup (file);
855
856 if (xmlFile)
857 {
858 pPrivate->xmlFile = strdup (xmlFile);
859 asprintf (&pPrivate->xmlPath, "/compiz/plugin[@name = '%s']", name);
860 }
861
862 plugin->context = context;
863 plugin->name = strdup (name);
864
865 if (!basicMetadata)
866 {
867 plugin->shortDesc =
868 strdup (pluginInfoPB.has_short_desc () ?
869 pluginInfoPB.short_desc ().c_str () :
870 name);
871 plugin->longDesc =
872 strdup (pluginInfoPB.has_long_desc () ?
873 pluginInfoPB.long_desc ().c_str () :
874 name);
875 plugin->category = strdup (pluginInfoPB.has_category () ?
876 pluginInfoPB.category ().c_str () :
877 "");
878 }
879 else
880 {
881 plugin->shortDesc = strdup (name);
882 plugin->longDesc = strdup (name);
883 plugin->category = strdup ("");
884 }
885
886 initRulesFromPB (plugin, pluginInfoPB);
887
888 context->plugins = ccsPluginListAppend (context->plugins, plugin);
889 }
890
891 static void
addCoreSettingsFromPB(CCSContext * context,const PluginInfoMetadata & pluginInfoPB,char * file,char * xmlFile)892 addCoreSettingsFromPB (CCSContext * context,
893 const PluginInfoMetadata & pluginInfoPB,
894 char *file,
895 char *xmlFile)
896 {
897 CCSPlugin *plugin;
898 CCSPluginPrivate *pPrivate;
899
900 if (ccsFindPlugin (context, "core"))
901 return;
902
903 plugin = (CCSPlugin*) calloc (1, sizeof (CCSPlugin));
904 if (!plugin)
905 return;
906
907 pPrivate = (CCSPluginPrivate *) calloc (1, sizeof (CCSPluginPrivate));
908 if (!pPrivate)
909 {
910 free (plugin);
911 return;
912 }
913
914 plugin->ccsPrivate = (void *) pPrivate;
915
916 if (file)
917 pPrivate->pbFilePath = strdup (file);
918
919 if (xmlFile)
920 {
921 pPrivate->xmlFile = strdup (xmlFile);
922 pPrivate->xmlPath = strdup ("/compiz/core");
923 }
924
925 plugin->context = context;
926 plugin->name = strdup ("core");
927 plugin->category = strdup ("General");
928
929 if (!basicMetadata)
930 {
931 plugin->shortDesc =
932 strdup (pluginInfoPB.has_short_desc () ?
933 pluginInfoPB.short_desc ().c_str () :
934 "General Options");
935
936 plugin->longDesc =
937 strdup (pluginInfoPB.has_long_desc () ?
938 pluginInfoPB.long_desc ().c_str () :
939 "General Compiz Options");
940 }
941 else
942 {
943 plugin->shortDesc = strdup ("General Options");
944 plugin->longDesc = strdup ("General Compiz Options");
945 }
946
947 initRulesFromPB (plugin, pluginInfoPB);
948 context->plugins = ccsPluginListAppend (context->plugins, plugin);
949 }
950
951 #endif
952
953
954 static int
pluginNameFilter(const struct dirent * name)955 pluginNameFilter (const struct dirent *name)
956 {
957 int length = strlen (name->d_name);
958
959 if (length < 7)
960 return 0;
961
962 if (strncmp (name->d_name, "lib", 3) ||
963 strncmp (name->d_name + length - 3, ".so", 3))
964 return 0;
965
966 return 1;
967 }
968
969 static int
pluginXMLFilter(const struct dirent * name)970 pluginXMLFilter (const struct dirent *name)
971 {
972 int length = strlen (name->d_name);
973
974 if (length < 5)
975 return 0;
976
977 if (strncmp (name->d_name + length - 4, ".xml", 4))
978 return 0;
979
980 return 1;
981 }
982
983
984 /* XML parsing */
985
986 static CCSSettingType
getOptionType(const char * name)987 getOptionType (const char *name)
988 {
989 static struct _TypeMap
990 {
991 const char *name;
992 CCSSettingType type;
993 } map[] = {
994 { "bool", TypeBool },
995 { "int", TypeInt },
996 { "float", TypeFloat },
997 { "string", TypeString },
998 { "color", TypeColor },
999 { "action", TypeAction },
1000 { "key", TypeKey },
1001 { "button", TypeButton },
1002 { "edge", TypeEdge },
1003 { "bell", TypeBell },
1004 { "match", TypeMatch },
1005 { "list", TypeList }
1006 };
1007 for (unsigned i = 0; i < sizeof (map) / sizeof (map[0]); i++)
1008 if (strcasecmp (name, map[i].name) == 0)
1009 return map[i].type;
1010
1011 return TypeNum;
1012 }
1013
1014 static char *
getStringFromXPath(xmlDoc * doc,xmlNode * base,const char * path)1015 getStringFromXPath (xmlDoc * doc, xmlNode * base, const char *path)
1016 {
1017 xmlXPathObjectPtr xpathObj;
1018 xmlXPathContextPtr xpathCtx;
1019 char *rv = NULL;
1020
1021 xpathCtx = xmlXPathNewContext (doc);
1022 if (!xpathCtx)
1023 return NULL;
1024
1025 if (base)
1026 xpathCtx->node = base;
1027
1028 xpathObj = xmlXPathEvalExpression (BAD_CAST path, xpathCtx);
1029
1030 if (!xpathObj)
1031 {
1032 xmlXPathFreeContext (xpathCtx);
1033 return NULL;
1034 }
1035
1036 xpathObj = xmlXPathConvertString (xpathObj);
1037
1038 if (xpathObj->type == XPATH_STRING && xpathObj->stringval
1039 && strlen ((char *) xpathObj->stringval))
1040 {
1041 rv = strdup ((char *) xpathObj->stringval);
1042 }
1043
1044 xmlXPathFreeObject (xpathObj);
1045 xmlXPathFreeContext (xpathCtx);
1046 return rv;
1047 }
1048
1049 static xmlNode **
getNodesFromXPath(xmlDoc * doc,xmlNode * base,const char * path,int * num)1050 getNodesFromXPath (xmlDoc * doc, xmlNode * base, const char *path, int *num)
1051 {
1052 xmlXPathObjectPtr xpathObj;
1053 xmlXPathContextPtr xpathCtx;
1054 xmlNode **rv = NULL;
1055 int size;
1056 int i;
1057
1058 *num = 0;
1059
1060 xpathCtx = xmlXPathNewContext (doc);
1061 if (!xpathCtx)
1062 return NULL;
1063
1064 if (base)
1065 xpathCtx->node = base;
1066
1067 xpathObj = xmlXPathEvalExpression (BAD_CAST path, xpathCtx);
1068 if (!xpathObj)
1069 {
1070 xmlXPathFreeContext (xpathCtx);
1071 return NULL;
1072 }
1073
1074 size = (xpathObj->nodesetval) ? xpathObj->nodesetval->nodeNr : 0;
1075 if (!size)
1076 {
1077 xmlXPathFreeObject (xpathObj);
1078 xmlXPathFreeContext (xpathCtx);
1079 return NULL;
1080 }
1081
1082 rv = (xmlNode **) malloc (size * sizeof (xmlNode *));
1083 if (!rv)
1084 {
1085 xmlXPathFreeObject (xpathObj);
1086 xmlXPathFreeContext (xpathCtx);
1087 return NULL;
1088 }
1089 *num = size;
1090
1091 for (i = 0; i < size; i++)
1092 rv[i] = xpathObj->nodesetval->nodeTab[i];
1093
1094 xmlXPathFreeObject (xpathObj);
1095 xmlXPathFreeContext (xpathCtx);
1096
1097 return rv;
1098 }
1099
1100 static Bool
nodeExists(xmlNode * node,const char * path)1101 nodeExists (xmlNode * node, const char *path)
1102 {
1103 xmlNode **nodes = NULL;
1104 int num;
1105 nodes = getNodesFromXPath (node->doc, node, path, &num);
1106
1107 if (num)
1108 {
1109 free (nodes);
1110 return TRUE;
1111 }
1112
1113 return FALSE;
1114 }
1115
1116 static char *
stringFromNodeDef(xmlNode * node,const char * path,const char * def)1117 stringFromNodeDef (xmlNode * node, const char *path, const char *def)
1118 {
1119 char *val;
1120 char *rv = NULL;
1121
1122 val = getStringFromXPath (node->doc, node, path);
1123
1124 if (val)
1125 {
1126 rv = strdup (val);
1127 free (val);
1128 }
1129 else if (def)
1130 rv = strdup (def);
1131
1132 return rv;
1133 }
1134
1135 static char *
stringFromNodeDefTrans(xmlNode * node,const char * path,const char * def)1136 stringFromNodeDefTrans (xmlNode * node, const char *path, const char *def)
1137 {
1138 const char *lang = getLocale ();
1139 char newPath[1024];
1140 char *rv = NULL;
1141
1142 if (!lang || !strlen (lang))
1143 return stringFromNodeDef (node, path, def);
1144
1145 snprintf (newPath, 1023, "%s[lang('%s')]", path, lang);
1146 rv = stringFromNodeDef (node, newPath, NULL);
1147 if (rv)
1148 return rv;
1149
1150 snprintf (newPath, 1023, "%s[lang(substring-before('%s','.'))]", path, lang);
1151 rv = stringFromNodeDef (node, newPath, NULL);
1152 if (rv)
1153 return rv;
1154
1155 snprintf (newPath, 1023, "%s[lang(substring-before('%s','_'))]", path, lang);
1156 rv = stringFromNodeDef (node, newPath, NULL);
1157 if (rv)
1158 return rv;
1159
1160 snprintf (newPath, 1023, "%s[lang('C')]", path);
1161 rv = stringFromNodeDef (node, newPath, NULL);
1162 if (rv)
1163 return rv;
1164
1165 return stringFromNodeDef (node, path, def);
1166 }
1167
1168 static void
initBoolValue(CCSSettingValue * v,xmlNode * node,void * valuePBv)1169 initBoolValue (CCSSettingValue * v,
1170 xmlNode * node,
1171 void * valuePBv)
1172 {
1173 char *value;
1174
1175 v->value.asBool = FALSE;
1176
1177 value = getStringFromXPath (node->doc, node, "child::text()");
1178
1179 if (value)
1180 {
1181 if (strcasecmp ((char *) value, "true") == 0)
1182 {
1183 v->value.asBool = TRUE;
1184 #ifdef USE_PROTOBUF
1185 if (valuePBv)
1186 ((GenericValueMetadata *) valuePBv)->set_bool_value (TRUE);
1187 #endif
1188 }
1189 free (value);
1190 }
1191 }
1192
1193 static void
initIntValue(CCSSettingValue * v,CCSSettingInfo * i,xmlNode * node,void * valuePBv)1194 initIntValue (CCSSettingValue * v,
1195 CCSSettingInfo * i,
1196 xmlNode * node,
1197 void * valuePBv)
1198 {
1199 char *value;
1200
1201 v->value.asInt = (i->forInt.min + i->forInt.max) / 2;
1202
1203 value = getStringFromXPath (node->doc, node, "child::text()");
1204
1205 if (value)
1206 {
1207 int val = strtol ((char *) value, NULL, 0);
1208
1209 if (val >= i->forInt.min && val <= i->forInt.max)
1210 {
1211 v->value.asInt = val;
1212 #ifdef USE_PROTOBUF
1213 if (valuePBv)
1214 ((GenericValueMetadata *) valuePBv)->set_int_value (val);
1215 #endif
1216 }
1217
1218 free (value);
1219 }
1220 }
1221
1222 static void
initFloatValue(CCSSettingValue * v,CCSSettingInfo * i,xmlNode * node,void * valuePBv)1223 initFloatValue (CCSSettingValue * v,
1224 CCSSettingInfo * i,
1225 xmlNode * node,
1226 void * valuePBv)
1227 {
1228 char *value;
1229 char *loc;
1230
1231 v->value.asFloat = (i->forFloat.min + i->forFloat.max) / 2;
1232
1233 loc = setlocale (LC_NUMERIC, NULL);
1234 setlocale (LC_NUMERIC, "C");
1235 value = getStringFromXPath (node->doc, node, "child::text()");
1236
1237 if (value)
1238 {
1239 float val = strtod ((char *) value, NULL);
1240
1241 if (val >= i->forFloat.min && val <= i->forFloat.max)
1242 {
1243 v->value.asFloat = val;
1244 #ifdef USE_PROTOBUF
1245 if (valuePBv)
1246 ((GenericValueMetadata *) valuePBv)->set_float_value (val);
1247 #endif
1248 }
1249
1250 free (value);
1251 }
1252
1253 setlocale (LC_NUMERIC, loc);
1254 }
1255
1256 static void
initStringValue(CCSSettingValue * v,CCSSettingInfo * i,xmlNode * node,void * valuePBv)1257 initStringValue (CCSSettingValue * v,
1258 CCSSettingInfo * i,
1259 xmlNode * node,
1260 void * valuePBv)
1261 {
1262 char *value;
1263
1264 value = getStringFromXPath (node->doc, node, "child::text()");
1265
1266 if (value)
1267 {
1268 free (v->value.asString);
1269 v->value.asString = strdup (value);
1270
1271 #ifdef USE_PROTOBUF
1272 if (valuePBv)
1273 ((GenericValueMetadata *) valuePBv)->set_string_value (value);
1274 #endif
1275 free (value);
1276 }
1277 else
1278 v->value.asString = strdup ("");
1279 }
1280
1281 static void
initColorValue(CCSSettingValue * v,xmlNode * node,void * valuePBv)1282 initColorValue (CCSSettingValue * v, xmlNode * node, void * valuePBv)
1283 {
1284 char *value;
1285
1286 memset (&v->value.asColor, 0, sizeof (v->value.asColor));
1287 v->value.asColor.color.alpha = 0xffff;
1288
1289 #ifdef USE_PROTOBUF
1290 OptionMetadata::ColorValue *colorPB = NULL;
1291 if (valuePBv)
1292 colorPB = ((GenericValueMetadata *) valuePBv)->mutable_color_value ();
1293 #endif
1294
1295 value = getStringFromXPath (node->doc, node, "red/child::text()");
1296 if (value)
1297 {
1298 int color = strtol ((char *) value, NULL, 0);
1299
1300 v->value.asColor.color.red = MAX (0, MIN (0xffff, color));
1301 #ifdef USE_PROTOBUF
1302 if (colorPB)
1303 colorPB->set_red (value);
1304 #endif
1305 free (value);
1306 }
1307
1308 value = getStringFromXPath (node->doc, node, "green/child::text()");
1309 if (value)
1310 {
1311 int color = strtol ((char *) value, NULL, 0);
1312
1313 v->value.asColor.color.green = MAX (0, MIN (0xffff, color));
1314 #ifdef USE_PROTOBUF
1315 if (colorPB)
1316 colorPB->set_green (value);
1317 #endif
1318 free (value);
1319 }
1320
1321 value = getStringFromXPath (node->doc, node, "blue/child::text()");
1322 if (value)
1323 {
1324 int color = strtol ((char *) value, NULL, 0);
1325
1326 v->value.asColor.color.blue = MAX (0, MIN (0xffff, color));
1327 #ifdef USE_PROTOBUF
1328 if (colorPB)
1329 colorPB->set_blue (value);
1330 #endif
1331 free (value);
1332 }
1333
1334 value = getStringFromXPath (node->doc, node, "alpha/child::text()");
1335 if (value)
1336 {
1337 int color = strtol (value, NULL, 0);
1338
1339 v->value.asColor.color.alpha = MAX (0, MIN (0xffff, color));
1340 #ifdef USE_PROTOBUF
1341 if (colorPB)
1342 colorPB->set_alpha (value);
1343 #endif
1344 free (value);
1345 }
1346 }
1347
1348 static void
initMatchValue(CCSSettingValue * v,xmlNode * node,void * valuePBv)1349 initMatchValue (CCSSettingValue * v, xmlNode * node, void * valuePBv)
1350 {
1351 char *value;
1352
1353 value = getStringFromXPath (node->doc, node, "child::text()");
1354 if (value)
1355 {
1356 free (v->value.asMatch);
1357 v->value.asMatch = strdup (value);
1358
1359 #ifdef USE_PROTOBUF
1360 if (valuePBv)
1361 ((GenericValueMetadata *) valuePBv)->set_string_value (value);
1362 #endif
1363 free (value);
1364 }
1365 else
1366 v->value.asMatch = strdup ("");
1367 }
1368
1369 static void
initKeyValue(CCSSettingValue * v,CCSSettingInfo * i,xmlNode * node,void * valuePBv)1370 initKeyValue (CCSSettingValue * v,
1371 CCSSettingInfo * i,
1372 xmlNode * node,
1373 void * valuePBv)
1374 {
1375 char *value;
1376
1377 memset (&v->value.asKey, 0, sizeof (v->value.asKey));
1378
1379 value = getStringFromXPath (node->doc, node, "child::text()");
1380 if (value)
1381 {
1382 #ifdef USE_PROTOBUF
1383 if (valuePBv)
1384 ((GenericValueMetadata *) valuePBv)->set_string_value (value);
1385 #endif
1386 if (strcasecmp (value, "disabled"))
1387 {
1388 ccsStringToKeyBinding (value, &v->value.asKey);
1389 }
1390 free (value);
1391 }
1392 }
1393
1394 static void
initButtonValue(CCSSettingValue * v,CCSSettingInfo * i,xmlNode * node,void * valuePBv)1395 initButtonValue (CCSSettingValue * v,
1396 CCSSettingInfo * i,
1397 xmlNode * node,
1398 void * valuePBv)
1399 {
1400 char *value;
1401
1402 memset (&v->value.asButton, 0, sizeof (v->value.asButton));
1403
1404 value = getStringFromXPath (node->doc, node, "child::text()");
1405 if (value)
1406 {
1407 #ifdef USE_PROTOBUF
1408 if (valuePBv)
1409 ((GenericValueMetadata *) valuePBv)->set_string_value (value);
1410 #endif
1411 if (strcasecmp (value, "disabled"))
1412 {
1413 ccsStringToButtonBinding (value, &v->value.asButton);
1414 }
1415 free (value);
1416 }
1417 }
1418
1419 static void
initEdgeValue(CCSSettingValue * v,CCSSettingInfo * i,xmlNode * node,void * valuePBv)1420 initEdgeValue (CCSSettingValue * v,
1421 CCSSettingInfo * i,
1422 xmlNode * node,
1423 void * valuePBv)
1424 {
1425 xmlNode **nodes;
1426 char *value;
1427 int k, num;
1428
1429 v->value.asEdge = 0;
1430
1431 static const char *edge[] = {
1432 "Left",
1433 "Right",
1434 "Top",
1435 "Bottom",
1436 "TopLeft",
1437 "TopRight",
1438 "BottomLeft",
1439 "BottomRight"
1440 };
1441
1442 nodes = getNodesFromXPath (node->doc, node, "edge", &num);
1443
1444 for (k = 0; k < num; k++)
1445 {
1446 value = getStringFromXPath (node->doc, nodes[k], "@name");
1447 if (value)
1448 {
1449 for (unsigned j = 0; j < sizeof (edge) / sizeof (edge[0]); j++)
1450 {
1451 if (strcasecmp ((char *) value, edge[j]) == 0)
1452 v->value.asEdge |= (1 << j);
1453 }
1454 free (value);
1455 }
1456 }
1457 if (num)
1458 free (nodes);
1459
1460 #ifdef USE_PROTOBUF
1461 if (valuePBv)
1462 ((GenericValueMetadata *) valuePBv)->set_edge_value (v->value.asEdge);
1463 #endif
1464 }
1465
1466 static void
initBellValue(CCSSettingValue * v,CCSSettingInfo * i,xmlNode * node,void * valuePBv)1467 initBellValue (CCSSettingValue * v,
1468 CCSSettingInfo * i,
1469 xmlNode * node,
1470 void * valuePBv)
1471 {
1472 char *value;
1473
1474 v->value.asBell = FALSE;
1475
1476 value = getStringFromXPath (node->doc, node, "child::text()");
1477 if (value)
1478 {
1479 if (!strcasecmp (value, "true"))
1480 {
1481 v->value.asBell = TRUE;
1482 #ifdef USE_PROTOBUF
1483 if (valuePBv)
1484 ((GenericValueMetadata *) valuePBv)->set_bool_value (TRUE);
1485 #endif
1486 }
1487 free (value);
1488 }
1489 }
1490
1491 static void
initListValue(CCSSettingValue * v,CCSSettingInfo * i,xmlNode * node,void * optionPBv)1492 initListValue (CCSSettingValue * v,
1493 CCSSettingInfo * i,
1494 xmlNode * node,
1495 void * optionPBv)
1496 {
1497 xmlNode **nodes;
1498 int num, j;
1499
1500 nodes = getNodesFromXPath (node->doc, node, "value", &num);
1501 if (num)
1502 {
1503 for (j = 0; j < num; j++)
1504 {
1505 void *valuePBv = NULL;
1506 #ifdef USE_PROTOBUF
1507 if (optionPBv)
1508 valuePBv = ((OptionMetadata *) optionPBv)->add_default_value ();
1509 #endif
1510 CCSSettingValue *val;
1511 val = (CCSSettingValue *) calloc (1, sizeof (CCSSettingValue));
1512 if (!val)
1513 continue;
1514
1515 val->parent = v->parent;
1516 val->isListChild = TRUE;
1517
1518 switch (i->forList.listType)
1519 {
1520 case TypeBool:
1521 initBoolValue (val, nodes[j], valuePBv);
1522 break;
1523 case TypeInt:
1524 initIntValue (val, i->forList.listInfo, nodes[j], valuePBv);
1525 break;
1526 case TypeFloat:
1527 initFloatValue (val, i->forList.listInfo, nodes[j], valuePBv);
1528 break;
1529 case TypeString:
1530 initStringValue (val, i->forList.listInfo, nodes[j], valuePBv);
1531 break;
1532 case TypeColor:
1533 initColorValue (val, nodes[j], valuePBv);
1534 break;
1535 case TypeKey:
1536 initKeyValue (val, i->forList.listInfo, nodes[j], valuePBv);
1537 break;
1538 case TypeButton:
1539 initButtonValue (val, i->forList.listInfo, nodes[j], valuePBv);
1540 break;
1541 case TypeEdge:
1542 initEdgeValue (val, i->forList.listInfo, nodes[j], valuePBv);
1543 break;
1544 case TypeBell:
1545 initBellValue (val, i->forList.listInfo, nodes[j], valuePBv);
1546 break;
1547 case TypeMatch:
1548 initMatchValue (val, nodes[j], valuePBv);
1549 default:
1550 break;
1551 }
1552 v->value.asList = ccsSettingValueListAppend (v->value.asList, val);
1553 }
1554 free (nodes);
1555 }
1556 }
1557
1558 static void
initIntInfo(CCSSettingInfo * i,xmlNode * node,void * optionPBv)1559 initIntInfo (CCSSettingInfo * i, xmlNode * node, void * optionPBv)
1560 {
1561 xmlNode **nodes;
1562 char *name;
1563 char *value;
1564 int num, j;
1565 i->forInt.min = MINSHORT;
1566 i->forInt.max = MAXSHORT;
1567 i->forInt.desc = NULL;
1568
1569 value = getStringFromXPath (node->doc, node, "min/child::text()");
1570 if (value)
1571 {
1572 int val = strtol (value, NULL, 0);
1573 i->forInt.min = val;
1574 free (value);
1575 #ifdef USE_PROTOBUF
1576 if (optionPBv)
1577 ((OptionMetadata *) optionPBv)->set_int_min (val);
1578 #endif
1579 }
1580
1581 value = getStringFromXPath (node->doc, node, "max/child::text()");
1582 if (value)
1583 {
1584 int val = strtol (value, NULL, 0);
1585 i->forInt.max = val;
1586 free (value);
1587 #ifdef USE_PROTOBUF
1588 if (optionPBv)
1589 ((OptionMetadata *) optionPBv)->set_int_max (val);
1590 #endif
1591 }
1592
1593 if (!basicMetadata)
1594 {
1595 nodes = getNodesFromXPath (node->doc, node, "desc", &num);
1596 if (num)
1597 {
1598 for (j = 0; j < num; j++)
1599 {
1600 value = getStringFromXPath (node->doc, nodes[j],
1601 "value/child::text()");
1602 if (value)
1603 {
1604 int val = strtol (value, NULL, 0);
1605 free (value);
1606
1607 if (val >= i->forInt.min && val <= i->forInt.max)
1608 {
1609 name = stringFromNodeDefTrans (nodes[j],
1610 "name/child::text()",
1611 NULL);
1612 if (name)
1613 {
1614 CCSIntDesc *intDesc;
1615
1616 intDesc = (CCSIntDesc *) calloc (1, sizeof (CCSIntDesc));
1617 if (intDesc)
1618 {
1619 intDesc->name = strdup (name);
1620 intDesc->value = val;
1621 i->forInt.desc =
1622 ccsIntDescListAppend (i->forInt.desc,
1623 intDesc);
1624 #ifdef USE_PROTOBUF
1625 if (optionPBv)
1626 {
1627 OptionMetadata::IntDescription *intDescPB =
1628 ((OptionMetadata *) optionPBv)->
1629 add_int_desc ();
1630 intDescPB->set_value (val);
1631 intDescPB->set_name (name);
1632 }
1633 #endif
1634 }
1635 free (name);
1636 }
1637 }
1638 }
1639 }
1640 free (nodes);
1641 }
1642 }
1643 }
1644
1645 static void
initFloatInfo(CCSSettingInfo * i,xmlNode * node,void * optionPBv)1646 initFloatInfo (CCSSettingInfo * i, xmlNode * node, void * optionPBv)
1647 {
1648 char *value;
1649 char *loc;
1650
1651 i->forFloat.min = MINSHORT;
1652 i->forFloat.max = MAXSHORT;
1653 i->forFloat.precision = 0.1f;
1654
1655 loc = setlocale (LC_NUMERIC, NULL);
1656 setlocale (LC_NUMERIC, "C");
1657 value = getStringFromXPath (node->doc, node, "min/child::text()");
1658 if (value)
1659 {
1660 float val = strtod (value, NULL);
1661 i->forFloat.min = val;
1662 free (value);
1663 #ifdef USE_PROTOBUF
1664 if (optionPBv)
1665 ((OptionMetadata *) optionPBv)->set_float_min (val);
1666 #endif
1667 }
1668
1669 value = getStringFromXPath (node->doc, node, "max/child::text()");
1670 if (value)
1671 {
1672 float val = strtod (value, NULL);
1673 i->forFloat.max = val;
1674 free (value);
1675 #ifdef USE_PROTOBUF
1676 if (optionPBv)
1677 ((OptionMetadata *) optionPBv)->set_float_max (val);
1678 #endif
1679 }
1680
1681 value = getStringFromXPath (node->doc, node, "precision/child::text()");
1682 if (value)
1683 {
1684 float val = strtod (value, NULL);
1685 i->forFloat.precision = val;
1686 free (value);
1687 #ifdef USE_PROTOBUF
1688 if (optionPBv)
1689 ((OptionMetadata *) optionPBv)->set_precision (val);
1690 #endif
1691 }
1692
1693 setlocale (LC_NUMERIC, loc);
1694 }
1695
1696 static void
initStringInfo(CCSSettingInfo * i,xmlNode * node,void * optionPBv)1697 initStringInfo (CCSSettingInfo * i, xmlNode * node, void * optionPBv)
1698 {
1699 xmlNode **nodes;
1700 char *name;
1701 char *value;
1702 int num, j;
1703 i->forString.restriction = NULL;
1704 i->forString.sortStartsAt = -1;
1705 i->forString.extensible = FALSE;
1706
1707 if (!basicMetadata)
1708 {
1709 if (nodeExists (node, "extensible"))
1710 {
1711 i->forString.extensible = TRUE;
1712 #ifdef USE_PROTOBUF
1713 if (optionPBv)
1714 ((OptionMetadata *) optionPBv)->set_extensible (TRUE);
1715 #endif
1716 }
1717
1718 nodes = getNodesFromXPath (node->doc, node, "sort", &num);
1719 if (num)
1720 {
1721 int val = 0; /* Start sorting at 0 unless otherwise specified. */
1722
1723 value = getStringFromXPath (node->doc, nodes[0], "@start");
1724 if (value)
1725 {
1726 /* Custom starting value specified. */
1727 val = strtol (value, NULL, 0);
1728 if (val < 0)
1729 val = 0;
1730 free (value);
1731 }
1732 i->forString.sortStartsAt = val;
1733 #ifdef USE_PROTOBUF
1734 if (optionPBv)
1735 ((OptionMetadata *) optionPBv)->set_sort_start (val);
1736 #endif
1737 free (nodes);
1738 }
1739
1740 nodes = getNodesFromXPath (node->doc, node, "restriction", &num);
1741 if (num)
1742 {
1743 for (j = 0; j < num; j++)
1744 {
1745 #ifdef USE_PROTOBUF
1746 OptionMetadata::StringRestriction * strRestrictionPB = NULL;
1747 if (optionPBv)
1748 strRestrictionPB =
1749 ((OptionMetadata *) optionPBv)->add_str_restriction ();
1750 #endif
1751 value = getStringFromXPath (node->doc, nodes[j],
1752 "value/child::text()");
1753 if (value)
1754 {
1755 name = stringFromNodeDefTrans (nodes[j],
1756 "name/child::text()",
1757 NULL);
1758 if (name)
1759 {
1760 ccsAddRestrictionToStringInfo (&i->forString,
1761 name, value);
1762 #ifdef USE_PROTOBUF
1763 if (strRestrictionPB)
1764 {
1765 strRestrictionPB->set_value (value);
1766 strRestrictionPB->set_name (name);
1767 }
1768 #endif
1769 free (name);
1770 }
1771 free (value);
1772 }
1773 }
1774 free (nodes);
1775 }
1776 }
1777 }
1778
1779 static void
initListInfo(CCSSettingInfo * i,xmlNode * node,void * optionPBv)1780 initListInfo (CCSSettingInfo * i, xmlNode * node, void * optionPBv)
1781 {
1782 char *value;
1783 CCSSettingInfo *info;
1784
1785 i->forList.listType = TypeBool;
1786 i->forList.listInfo = NULL;
1787
1788 value = getStringFromXPath (node->doc, node, "type/child::text()");
1789
1790 if (!value)
1791 return;
1792
1793 i->forList.listType = getOptionType (value);
1794 #ifdef USE_PROTOBUF
1795 if (optionPBv)
1796 ((OptionMetadata *) optionPBv)->set_list_type
1797 ((OptionMetadata::Type) i->forList.listType);
1798 #endif
1799
1800 free (value);
1801
1802 switch (i->forList.listType)
1803 {
1804 case TypeInt:
1805 {
1806 info = (CCSSettingInfo *) calloc (1, sizeof (CCSSettingInfo));
1807 if (info)
1808 initIntInfo (info, node, optionPBv);
1809 i->forList.listInfo = info;
1810 }
1811 break;
1812 case TypeFloat:
1813 {
1814 info = (CCSSettingInfo *) calloc (1, sizeof (CCSSettingInfo));
1815 if (info)
1816 initFloatInfo (info, node, optionPBv);
1817 i->forList.listInfo = info;
1818 }
1819 break;
1820 case TypeString:
1821 {
1822 info = (CCSSettingInfo *) calloc (1, sizeof (CCSSettingInfo));
1823 if (info)
1824 initStringInfo (info, node, optionPBv);
1825 i->forList.listInfo = info;
1826 }
1827 break;
1828 default:
1829 break;
1830 }
1831 }
1832
1833 static void
initActionInfo(CCSSettingInfo * i,xmlNode * node,void * optionPBv)1834 initActionInfo (CCSSettingInfo * i, xmlNode * node, void * optionPBv)
1835 {
1836 char *value;
1837
1838 i->forAction.internal = FALSE;
1839
1840 value = getStringFromXPath (node->doc, node, "internal/child::text()");
1841 if (value)
1842 {
1843 if (strcasecmp (value, "true") == 0)
1844 {
1845 i->forAction.internal = TRUE;
1846 #ifdef USE_PROTOBUF
1847 if (optionPBv)
1848 ((OptionMetadata *) optionPBv)->set_internal (TRUE);
1849 #endif
1850 }
1851 free (value);
1852 return;
1853 }
1854 if (nodeExists (node, "internal"))
1855 {
1856 i->forAction.internal = TRUE;
1857 #ifdef USE_PROTOBUF
1858 if (optionPBv)
1859 ((OptionMetadata *) optionPBv)->set_internal (TRUE);
1860 #endif
1861 }
1862 }
1863
1864 #ifdef USE_PROTOBUF
1865 static void
checkAddGroupSubgroup(OptionMetadata * optPB,StringList * descList,char * name,Bool isGroup)1866 checkAddGroupSubgroup (OptionMetadata *optPB,
1867 StringList *descList,
1868 char *name,
1869 Bool isGroup)
1870 {
1871 // Check if group has the same name as the last group in the groups list
1872 int len = descList->size ();
1873 if (len > 0 &&
1874 strcmp (name, descList->Get (len - 1).c_str ()) == 0)
1875 {
1876 if (isGroup)
1877 optPB->set_group_id (len - 1);
1878 else
1879 optPB->set_subgroup_id (len - 1);
1880 }
1881 else
1882 {
1883 // Add new group to the list
1884 descList->Add ()->assign (name);
1885
1886 if (isGroup)
1887 optPB->set_group_id (len);
1888 else
1889 optPB->set_subgroup_id (len);
1890 }
1891 }
1892
1893 static Bool
createProtoBufCacheDir()1894 createProtoBufCacheDir ()
1895 {
1896 if (metadataCacheDir.length () > 0)
1897 {
1898 // Cache dir must have been created already, since otherwise it would
1899 // be "". So we can return here.
1900 return TRUE;
1901 }
1902 char *cacheBaseDir = NULL;
1903 char *cacheHome = getenv ("XDG_CACHE_HOME");
1904
1905 if (cacheHome && strlen (cacheHome))
1906 {
1907 asprintf (&cacheBaseDir, "%s", cacheHome);
1908 }
1909 else
1910 {
1911 char *home = getenv ("HOME");
1912 if (home && strlen (home))
1913 {
1914 asprintf (&cacheBaseDir, "%s/.cache", home);
1915 }
1916 }
1917
1918 if (cacheBaseDir)
1919 {
1920 metadataCacheDir = cacheBaseDir;
1921 if (metadataCacheDir[metadataCacheDir.length () - 1] != '/')
1922 metadataCacheDir += "/";
1923 metadataCacheDir += "compizconfig";
1924 std::string metadataCacheFileDummy = metadataCacheDir + "/dummy";
1925
1926 // Create cache dir
1927 Bool success = ccsCreateDirFor (metadataCacheFileDummy.c_str ());
1928 if (!success)
1929 fprintf (stderr, "[ERROR]: Error creating directory \"%s\"\n",
1930 metadataCacheDir.c_str ());
1931 free (cacheBaseDir);
1932
1933 if (success)
1934 return TRUE; // metadataCacheDir will be used later in this case
1935
1936 metadataCacheDir = ""; // invalidate metadataCacheDir
1937 }
1938
1939 usingProtobuf = FALSE; // Disable protobuf if cache dir cannot be created
1940 return FALSE;
1941 }
1942
1943 #endif
1944
1945 static void
addOptionForPlugin(CCSPlugin * plugin,char * name,char * type,Bool isReadonly,Bool isScreen,unsigned int screen,xmlNode * node,void * groupListPBv,void * subgroupListPBv,void * optionPBv)1946 addOptionForPlugin (CCSPlugin * plugin,
1947 char * name,
1948 char * type,
1949 Bool isReadonly,
1950 Bool isScreen,
1951 unsigned int screen,
1952 xmlNode * node,
1953 void * groupListPBv,
1954 void * subgroupListPBv,
1955 void * optionPBv)
1956 {
1957 xmlNode **nodes;
1958 int num = 0;
1959 CCSSetting *setting;
1960
1961 if (ccsFindSetting (plugin, name, isScreen, screen))
1962 {
1963 fprintf (stderr, "[ERROR]: Option \"%s\" already defined\n", name);
1964 return;
1965 }
1966
1967 if (getOptionType (type) == TypeNum)
1968 return;
1969
1970 setting = (CCSSetting *) calloc (1, sizeof (CCSSetting));
1971 if (!setting)
1972 return;
1973
1974 setting->parent = plugin;
1975 setting->isScreen = isScreen;
1976 setting->screenNum = screen;
1977 setting->isDefault = TRUE;
1978 setting->name = strdup (name);
1979
1980 if (!basicMetadata)
1981 {
1982 setting->shortDesc =
1983 stringFromNodeDefTrans (node, "short/child::text()", name);
1984 setting->longDesc =
1985 stringFromNodeDefTrans (node, "long/child::text()", "");
1986 setting->hints = stringFromNodeDef (node, "hints/child::text()", "");
1987 setting->group =
1988 stringFromNodeDefTrans (node, "ancestor::group/short/child::text()",
1989 "");
1990 setting->subGroup =
1991 stringFromNodeDefTrans (node,
1992 "ancestor::subgroup/short/child::text()",
1993 "");
1994 }
1995 else
1996 {
1997 setting->shortDesc = strdup (name);
1998 setting->longDesc = strdup ("");
1999 setting->hints = strdup ("");
2000 setting->group = strdup ("");
2001 setting->subGroup = strdup ("");
2002 }
2003 setting->type = getOptionType (type);
2004
2005 #ifdef USE_PROTOBUF
2006 OptionMetadata *optPB = NULL;
2007
2008 if (optionPBv)
2009 {
2010 optPB = (OptionMetadata *) optionPBv;
2011
2012 optPB->set_name (name);
2013 optPB->set_type ((OptionMetadata::Type) setting->type);
2014 if (isReadonly)
2015 optPB->set_read_only (isReadonly);
2016
2017 optPB->set_short_desc (setting->shortDesc);
2018 optPB->set_long_desc (setting->longDesc);
2019
2020 if (strlen (setting->hints) > 0)
2021 optPB->set_hints (setting->hints);
2022
2023 if (groupListPBv && strlen (setting->group) > 0)
2024 checkAddGroupSubgroup (optPB, (StringList *) groupListPBv,
2025 setting->group, TRUE);
2026 if (subgroupListPBv && strlen (setting->subGroup) > 0)
2027 checkAddGroupSubgroup (optPB, (StringList *) subgroupListPBv,
2028 setting->subGroup, FALSE);
2029 }
2030 #endif
2031 setting->value = &setting->defaultValue;
2032 setting->defaultValue.parent = setting;
2033
2034 switch (setting->type)
2035 {
2036 case TypeInt:
2037 initIntInfo (&setting->info, node, optionPBv);
2038 break;
2039 case TypeFloat:
2040 initFloatInfo (&setting->info, node, optionPBv);
2041 break;
2042 case TypeString:
2043 initStringInfo (&setting->info, node, optionPBv);
2044 break;
2045 case TypeList:
2046 initListInfo (&setting->info, node, optionPBv);
2047 break;
2048 case TypeKey:
2049 case TypeButton:
2050 case TypeEdge:
2051 case TypeBell:
2052 initActionInfo (&setting->info, node, optionPBv);
2053 break;
2054 default:
2055 break;
2056 }
2057
2058 nodes = getNodesFromXPath (node->doc, node, "default", &num);
2059 if (num)
2060 {
2061 void * valuePBv = NULL;
2062 #ifdef USE_PROTOBUF
2063 if (optPB && setting->type != TypeList)
2064 valuePBv = optPB->add_default_value ();
2065 #endif
2066 switch (setting->type)
2067 {
2068 case TypeInt:
2069 initIntValue (&setting->defaultValue, &setting->info, nodes[0],
2070 valuePBv);
2071 break;
2072 case TypeBool:
2073 initBoolValue (&setting->defaultValue, nodes[0],
2074 valuePBv);
2075 break;
2076 case TypeFloat:
2077 initFloatValue (&setting->defaultValue, &setting->info, nodes[0],
2078 valuePBv);
2079 break;
2080 case TypeString:
2081 initStringValue (&setting->defaultValue, &setting->info, nodes[0],
2082 valuePBv);
2083 break;
2084 case TypeColor:
2085 initColorValue (&setting->defaultValue, nodes[0], valuePBv);
2086 break;
2087 case TypeKey:
2088 initKeyValue (&setting->defaultValue, &setting->info, nodes[0],
2089 valuePBv);
2090 break;
2091 case TypeButton:
2092 initButtonValue (&setting->defaultValue, &setting->info, nodes[0],
2093 valuePBv);
2094 break;
2095 case TypeEdge:
2096 initEdgeValue (&setting->defaultValue, &setting->info, nodes[0],
2097 valuePBv);
2098 break;
2099 case TypeBell:
2100 initBellValue (&setting->defaultValue, &setting->info, nodes[0],
2101 valuePBv);
2102 break;
2103 case TypeMatch:
2104 initMatchValue (&setting->defaultValue, nodes[0],
2105 valuePBv);
2106 break;
2107 case TypeList:
2108 initListValue (&setting->defaultValue, &setting->info, nodes[0],
2109 optionPBv);
2110 break;
2111 default:
2112 break;
2113 }
2114 }
2115 else
2116 {
2117 /* if we have no set defaults, we have at least to set
2118 the string defaults to empty strings */
2119 switch (setting->type)
2120 {
2121 case TypeString:
2122 setting->defaultValue.value.asString = strdup ("");
2123 break;
2124 case TypeMatch:
2125 setting->defaultValue.value.asMatch = strdup ("");
2126 break;
2127 default:
2128 break;
2129 }
2130 }
2131
2132 if (nodes)
2133 free (nodes);
2134
2135 if (isReadonly)
2136 {
2137 // Will come here only when protobuf is enabled
2138 ccsFreeSetting (setting);
2139 return;
2140 }
2141 // printSetting (setting);
2142 PLUGIN_PRIV (plugin);
2143 pPrivate->settings = ccsSettingListAppend (pPrivate->settings, setting);
2144 }
2145
2146 static void
addOptionFromXMLNode(CCSPlugin * plugin,xmlNode * node,Bool isScreen,void * groupListPBv,void * subgroupListPBv,void * optionPBv)2147 addOptionFromXMLNode (CCSPlugin * plugin,
2148 xmlNode * node,
2149 Bool isScreen,
2150 void * groupListPBv,
2151 void * subgroupListPBv,
2152 void * optionPBv)
2153 {
2154 char *name;
2155 char *type;
2156 char *readonly;
2157 Bool isReadonly;
2158
2159 if (!node)
2160 return;
2161
2162 name = getStringFromXPath (node->doc, node, "@name");
2163
2164 type = getStringFromXPath (node->doc, node, "@type");
2165
2166 readonly = getStringFromXPath (node->doc, node, "@read_only");
2167 isReadonly = readonly && !strcmp (readonly, "true");
2168
2169 // If optionPBv is non-NULL, we still want to get the option info to write
2170 // to .pb file, so we don't return immediately in that case.
2171
2172 if (!name || !strlen (name) || !type || !strlen (type) ||
2173 (!optionPBv && isReadonly))
2174 {
2175 if (name)
2176 free (name);
2177 if (type)
2178 free (type);
2179 if (readonly)
2180 free (readonly);
2181
2182 return;
2183 }
2184
2185 if (isScreen)
2186 {
2187 for (unsigned i = 0; i < plugin->context->numScreens; i++)
2188 addOptionForPlugin (plugin, name, type, isReadonly, TRUE,
2189 plugin->context->screens[i], node,
2190 groupListPBv, subgroupListPBv, optionPBv);
2191 }
2192 else
2193 {
2194 addOptionForPlugin (plugin, name, type, isReadonly, FALSE, 0, node,
2195 groupListPBv, subgroupListPBv, optionPBv);
2196 }
2197 free (name);
2198 free (type);
2199
2200 if (readonly)
2201 free (readonly);
2202 }
2203
2204 static void
initDisplayScreenFromRootNode(CCSPlugin * plugin,xmlNode * node,Bool isScreen,void * pluginPBv)2205 initDisplayScreenFromRootNode (CCSPlugin * plugin,
2206 xmlNode * node,
2207 Bool isScreen,
2208 void * pluginPBv)
2209 {
2210 xmlNode **nodes;
2211 xmlNode **optNodes;
2212 int num, i;
2213 void *groupListPBv = NULL;
2214 void *subgroupListPBv = NULL;
2215
2216 nodes = getNodesFromXPath (node->doc, node,
2217 (isScreen ? "screen" : "display"), &num);
2218 if (!num)
2219 return;
2220
2221 #ifdef USE_PROTOBUF
2222 ScreenMetadata *screenPB = NULL;
2223
2224 if (pluginPBv)
2225 {
2226 PluginMetadata *pluginPB = (PluginMetadata *) pluginPBv;
2227 screenPB = (isScreen ?
2228 pluginPB->mutable_screen () :
2229 pluginPB->mutable_display ());
2230 groupListPBv = screenPB->mutable_group_desc ();
2231 subgroupListPBv = screenPB->mutable_subgroup_desc ();
2232 }
2233 #endif
2234 optNodes = getNodesFromXPath
2235 (node->doc, nodes[0],
2236 "option | group/subgroup/option | group/option | subgroup/option",
2237 &num);
2238 if (num)
2239 {
2240 for (i = 0; i < num; i++)
2241 {
2242 void *optionPBv = NULL;
2243 #ifdef USE_PROTOBUF
2244 if (screenPB)
2245 optionPBv = screenPB->add_option ();
2246 #endif
2247 addOptionFromXMLNode (plugin, optNodes[i], isScreen,
2248 groupListPBv, subgroupListPBv, optionPBv);
2249 }
2250
2251 free (optNodes);
2252 }
2253 free (nodes);
2254 }
2255
2256 static inline void
initOptionsFromRootNode(CCSPlugin * plugin,xmlNode * node,void * pluginPBv)2257 initOptionsFromRootNode (CCSPlugin * plugin,
2258 xmlNode * node,
2259 void * pluginPBv)
2260 {
2261 // For display
2262 initDisplayScreenFromRootNode (plugin, node, FALSE, pluginPBv);
2263
2264 // For screen
2265 initDisplayScreenFromRootNode (plugin, node, TRUE, pluginPBv);
2266 }
2267
2268 static void
addStringsFromPath(CCSStringList * list,const char * path,xmlNode * node,void * stringListPBv)2269 addStringsFromPath (CCSStringList * list,
2270 const char * path,
2271 xmlNode * node,
2272 void * stringListPBv)
2273 {
2274 xmlNode **nodes;
2275 int num, i;
2276 nodes = getNodesFromXPath (node->doc, node, path, &num);
2277
2278 if (num)
2279 {
2280 for (i = 0; i < num; i++)
2281 {
2282 char *value = stringFromNodeDef (nodes[i], "child::text()", NULL);
2283
2284 if (value && strlen (value))
2285 {
2286 *list = ccsStringListAppend (*list, value);
2287 #ifdef USE_PROTOBUF
2288 if (stringListPBv)
2289 ((StringList *) stringListPBv)->Add ()->assign (value);
2290 #endif
2291 }
2292 if (value && !strlen (value))
2293 free (value);
2294 }
2295
2296 free (nodes);
2297 }
2298 }
2299
2300 static void
addStringExtensionFromXMLNode(CCSPlugin * plugin,xmlNode * node,void * extensionPBv)2301 addStringExtensionFromXMLNode (CCSPlugin * plugin,
2302 xmlNode * node,
2303 void * extensionPBv)
2304 {
2305 xmlNode **nodes;
2306 int num, j;
2307 CCSStrExtension *extension;
2308 char *name;
2309 char *value;
2310 char *isDisplay;
2311 void * stringListPBv = NULL;
2312
2313 extension = (CCSStrExtension *) calloc (1, sizeof (CCSStrExtension));
2314 if (!extension)
2315 return;
2316
2317 isDisplay = getStringFromXPath (node->doc, node, "@display");
2318
2319 extension->isScreen = !(isDisplay && !strcmp (isDisplay, "true"));
2320
2321 if (isDisplay)
2322 free (isDisplay);
2323
2324 extension->restriction = NULL;
2325
2326 extension->basePlugin = getStringFromXPath (node->doc, node, "@base_plugin");
2327 if (!extension->basePlugin)
2328 extension->basePlugin = strdup ("");
2329
2330 #ifdef USE_PROTOBUF
2331 ExtensionMetadata * extensionPB = NULL;
2332 if (extensionPBv)
2333 {
2334 extensionPB = (ExtensionMetadata *) extensionPBv;
2335 extensionPB->set_display (!extension->isScreen);
2336 extensionPB->set_base_plugin (extension->basePlugin);
2337 stringListPBv = extensionPB->mutable_base_option ();
2338 }
2339 #endif
2340
2341 addStringsFromPath (&extension->baseSettings, "base_option", node,
2342 stringListPBv);
2343
2344 nodes = getNodesFromXPath (node->doc, node, "restriction", &num);
2345 if (!num)
2346 {
2347 free (extension);
2348 return;
2349 }
2350
2351 for (j = 0; j < num; j++)
2352 {
2353 value = getStringFromXPath (node->doc, nodes[j], "value/child::text()");
2354 if (value)
2355 {
2356 name = stringFromNodeDefTrans (nodes[j], "name/child::text()",
2357 NULL);
2358 if (name)
2359 {
2360 ccsAddRestrictionToStringExtension (extension, name, value);
2361 #ifdef USE_PROTOBUF
2362 if (extensionPB)
2363 {
2364 OptionMetadata::StringRestriction *strRestrictionPB =
2365 extensionPB->add_str_restriction ();
2366 strRestrictionPB->set_value (value);
2367 strRestrictionPB->set_name (name);
2368 }
2369 #endif
2370 free (name);
2371 }
2372 free (value);
2373 }
2374 }
2375 free (nodes);
2376
2377 PLUGIN_PRIV (plugin);
2378
2379 pPrivate->stringExtensions =
2380 ccsStrExtensionListAppend (pPrivate->stringExtensions, extension);
2381 }
2382
2383 static void
initStringExtensionsFromRootNode(CCSPlugin * plugin,xmlNode * node,void * pluginPBv)2384 initStringExtensionsFromRootNode (CCSPlugin * plugin,
2385 xmlNode * node,
2386 void * pluginPBv)
2387 {
2388 xmlNode **nodes;
2389 int num, i;
2390 nodes = getNodesFromXPath (node->doc, node, "/compiz/*/extension", &num);
2391
2392 for (i = 0; i < num; i++)
2393 {
2394 void *extensionPBv = NULL;
2395 #ifdef USE_PROTOBUF
2396 if (pluginPBv)
2397 {
2398 PluginMetadata *pluginPB = (PluginMetadata *) pluginPBv;
2399 extensionPBv = pluginPB->add_extension ();
2400 }
2401 #endif
2402 addStringExtensionFromXMLNode (plugin, nodes[i], extensionPBv);
2403 }
2404 free (nodes);
2405 }
2406
2407 static void
initRulesFromRootNode(CCSPlugin * plugin,xmlNode * node,void * pluginInfoPBv)2408 initRulesFromRootNode (CCSPlugin * plugin, xmlNode * node, void * pluginInfoPBv)
2409 {
2410 void *featureListPBv = NULL;
2411 void *pluginAfterListPBv = NULL;
2412 void *pluginBeforeListPBv = NULL;
2413 void *requirePluginListPBv = NULL;
2414 void *requireFeatureListPBv = NULL;
2415 void *conflictPluginListPBv = NULL;
2416 void *conflictFeatureListPBv = NULL;
2417 #ifdef USE_PROTOBUF
2418 if (pluginInfoPBv)
2419 {
2420 PluginInfoMetadata *pluginInfoPB = (PluginInfoMetadata *) pluginInfoPBv;
2421 featureListPBv = pluginInfoPB->mutable_feature ();
2422
2423 DependenciesMetadata *deps = pluginInfoPB->mutable_deps ();
2424 pluginAfterListPBv = deps->mutable_after_plugin ();
2425 pluginBeforeListPBv = deps->mutable_before_plugin ();
2426 requirePluginListPBv = deps->mutable_require_plugin ();
2427 requireFeatureListPBv = deps->mutable_require_feature ();
2428 conflictPluginListPBv = deps->mutable_conflict_plugin ();
2429 conflictFeatureListPBv = deps->mutable_conflict_feature ();
2430 }
2431 #endif
2432
2433 addStringsFromPath (&plugin->providesFeature, "feature", node,
2434 featureListPBv);
2435
2436 addStringsFromPath (&plugin->loadAfter,
2437 "deps/relation[@type = 'after']/plugin", node,
2438 pluginAfterListPBv);
2439 addStringsFromPath (&plugin->loadBefore,
2440 "deps/relation[@type = 'before']/plugin", node,
2441 pluginBeforeListPBv);
2442 addStringsFromPath (&plugin->requiresPlugin,
2443 "deps/requirement/plugin", node, requirePluginListPBv);
2444 addStringsFromPath (&plugin->requiresFeature,
2445 "deps/requirement/feature", node, requireFeatureListPBv);
2446 addStringsFromPath (&plugin->conflictPlugin,
2447 "deps/conflict/plugin", node, conflictPluginListPBv);
2448 addStringsFromPath (&plugin->conflictFeature,
2449 "deps/conflict/feature", node, conflictFeatureListPBv);
2450 }
2451
2452 #ifdef USE_PROTOBUF
2453 static void
fillBasicInfoIntoPB(CCSPlugin * plugin,PluginInfoMetadata * pluginInfoPB)2454 fillBasicInfoIntoPB (CCSPlugin *plugin, PluginInfoMetadata *pluginInfoPB)
2455 {
2456 if (!pluginInfoPB)
2457 return;
2458
2459 pluginInfoPB->set_name (plugin->name);
2460 pluginInfoPB->set_short_desc (plugin->shortDesc);
2461 pluginInfoPB->set_long_desc (plugin->longDesc);
2462 pluginInfoPB->set_category (plugin->category);
2463 }
2464 #endif
2465
2466 /* Returns TRUE on success. */
2467 static Bool
addPluginFromXMLNode(CCSContext * context,xmlNode * node,char * file,void * pluginInfoPBv)2468 addPluginFromXMLNode (CCSContext * context,
2469 xmlNode * node,
2470 char * file,
2471 void * pluginInfoPBv)
2472 {
2473 char *name;
2474 CCSPlugin *plugin;
2475 CCSPluginPrivate *pPrivate;
2476
2477 if (!node)
2478 return FALSE;
2479
2480 name = getStringFromXPath (node->doc, node, "@name");
2481
2482 if (!name || !strlen (name))
2483 {
2484 if (name)
2485 free (name);
2486 return FALSE;
2487 }
2488
2489 if (!strcmp (name, "ini") || !strcmp (name, "gconf") ||
2490 !strcmp (name, "ccp") || !strcmp (name, "kconfig"))
2491 {
2492 free (name);
2493 return FALSE;
2494 }
2495
2496 if (ccsFindPlugin (context, name))
2497 {
2498 free (name);
2499 return FALSE;
2500 }
2501
2502 plugin = (CCSPlugin *) calloc (1, sizeof (CCSPlugin));
2503 if (!plugin)
2504 return FALSE;
2505
2506 pPrivate = (CCSPluginPrivate *) calloc (1, sizeof (CCSPluginPrivate));
2507 if (!pPrivate)
2508 {
2509 free (plugin);
2510 return FALSE;
2511 }
2512
2513 plugin->ccsPrivate = (void *) pPrivate;
2514
2515 if (file)
2516 pPrivate->xmlFile = strdup (file);
2517
2518 asprintf (&pPrivate->xmlPath, "/compiz/plugin[@name = '%s']", name);
2519 plugin->context = context;
2520 plugin->name = strdup (name);
2521
2522 if (!basicMetadata)
2523 {
2524 plugin->shortDesc =
2525 stringFromNodeDefTrans (node, "short/child::text()", name);
2526 plugin->longDesc =
2527 stringFromNodeDefTrans (node, "long/child::text()", name);
2528 plugin->category =
2529 stringFromNodeDef (node, "category/child::text()", "");
2530 }
2531 else
2532 {
2533 plugin->shortDesc = strdup (name);
2534 plugin->longDesc = strdup (name);
2535 plugin->category = strdup ("");
2536 }
2537 #ifdef USE_PROTOBUF
2538 fillBasicInfoIntoPB (plugin, (PluginInfoMetadata *) pluginInfoPBv);
2539 #endif
2540
2541 initRulesFromRootNode (plugin, node, pluginInfoPBv);
2542
2543 context->plugins = ccsPluginListAppend (context->plugins, plugin);
2544 free (name);
2545
2546 return TRUE;
2547 }
2548
2549 /* Returns TRUE on success. */
2550 static Bool
addCoreSettingsFromXMLNode(CCSContext * context,xmlNode * node,char * file,void * pluginInfoPBv)2551 addCoreSettingsFromXMLNode (CCSContext * context,
2552 xmlNode * node,
2553 char *file,
2554 void * pluginInfoPBv)
2555 {
2556 CCSPlugin *plugin;
2557 CCSPluginPrivate *pPrivate;
2558
2559 if (!node)
2560 return FALSE;
2561
2562 if (ccsFindPlugin (context, "core"))
2563 return FALSE;
2564
2565 plugin = (CCSPlugin *) calloc (1, sizeof (CCSPlugin));
2566 if (!plugin)
2567 return FALSE;
2568
2569 pPrivate = (CCSPluginPrivate *) calloc (1, sizeof (CCSPluginPrivate));
2570 if (!pPrivate)
2571 {
2572 free (plugin);
2573 return FALSE;
2574 }
2575
2576 plugin->ccsPrivate = (void *) pPrivate;
2577
2578 if (file)
2579 pPrivate->xmlFile = strdup (file);
2580
2581 pPrivate->xmlPath = strdup ("/compiz/core");
2582 plugin->context = context;
2583 plugin->name = strdup ("core");
2584 plugin->category = strdup ("General");
2585
2586 if (!basicMetadata)
2587 {
2588 plugin->shortDesc =
2589 stringFromNodeDefTrans (node, "short/child::text()",
2590 "General Options");
2591 plugin->longDesc =
2592 stringFromNodeDefTrans (node, "long/child::text()",
2593 "General Compiz Options");
2594 }
2595 else
2596 {
2597 plugin->shortDesc = strdup ("General Options");
2598 plugin->longDesc = strdup ("General Compiz Options");
2599 }
2600 #ifdef USE_PROTOBUF
2601 fillBasicInfoIntoPB (plugin, (PluginInfoMetadata *) pluginInfoPBv);
2602 #endif
2603
2604 initRulesFromRootNode (plugin, node, pluginInfoPBv);
2605 context->plugins = ccsPluginListAppend (context->plugins, plugin);
2606
2607 return TRUE;
2608 }
2609
2610 /* End of XML parsing */
2611
2612 #ifdef USE_PROTOBUF
2613
2614 // Either pluginMinMetadata or pluginMetadata should be non-NULL
2615 static Bool
loadPluginMetadataFromProtoBuf(char * pbPath,PluginBriefMetadata * pluginMinMetadata,PluginMetadata * pluginMetadata)2616 loadPluginMetadataFromProtoBuf (char *pbPath,
2617 PluginBriefMetadata *pluginMinMetadata,
2618 PluginMetadata *pluginMetadata)
2619 {
2620 Bool success = FALSE;
2621
2622 FILE *pbFile = fopen (pbPath, "rb");
2623 if (pbFile)
2624 {
2625 google::protobuf::io::FileInputStream inputStream (fileno (pbFile));
2626 if ((pluginMinMetadata &&
2627 pluginMinMetadata->ParseFromZeroCopyStream (&inputStream)) ||
2628 (pluginMetadata &&
2629 pluginMetadata->ParseFromZeroCopyStream (&inputStream)))
2630 success = TRUE;
2631 inputStream.Close ();
2632 }
2633
2634 return success;
2635 }
2636
2637 // Returns TRUE if successfully loads .pb file and .pb is up to date.
2638 static Bool
checkAndLoadProtoBuf(char * pbPath,struct stat * pbStat,struct stat * xmlStat,PluginBriefMetadata * pluginBriefPB)2639 checkAndLoadProtoBuf (char *pbPath,
2640 struct stat *pbStat,
2641 struct stat *xmlStat,
2642 PluginBriefMetadata *pluginBriefPB)
2643 {
2644 const PluginInfoMetadata &pluginInfoPB = pluginBriefPB->info ();
2645
2646 if (pbStat->st_mtime < xmlStat->st_mtime || // is .pb older than .xml?
2647 !loadPluginMetadataFromProtoBuf (pbPath, pluginBriefPB, NULL) ||
2648 (!basicMetadata && pluginBriefPB->info ().basic_metadata ()) ||
2649 pluginInfoPB.pb_abi_version () != PB_ABI_VERSION ||
2650 pluginInfoPB.time () != (unsigned long)xmlStat->st_mtime ||
2651 // xml modification time mismatch?
2652 (pluginInfoPB.locale () != "NONE" &&
2653 pluginInfoPB.locale () != shortLocale))
2654 {
2655 // .pb needs update
2656 return FALSE;
2657 }
2658 return TRUE;
2659 }
2660
2661 // Write .pb data to .pb file
2662 static void
writePBFile(char * pbFilePath,PluginMetadata * pluginPB,PluginBriefMetadata * pluginBriefPB,struct stat * xmlStat)2663 writePBFile (char *pbFilePath,
2664 PluginMetadata *pluginPB,
2665 PluginBriefMetadata *pluginBriefPB,
2666 struct stat *xmlStat)
2667 {
2668 if (!createProtoBufCacheDir ())
2669 return;
2670
2671 PluginInfoMetadata *pluginInfoPB;
2672
2673 if (pluginPB)
2674 {
2675 pluginInfoPB = pluginPB->mutable_info ();
2676 pluginInfoPB->set_brief_metadata (FALSE);
2677 }
2678 else
2679 {
2680 pluginInfoPB = pluginBriefPB->mutable_info ();
2681 pluginInfoPB->set_pb_abi_version (PB_ABI_VERSION);
2682 pluginInfoPB->set_locale (shortLocale);
2683 pluginInfoPB->set_time ((unsigned long)xmlStat->st_mtime);
2684 pluginInfoPB->set_brief_metadata (TRUE);
2685 }
2686
2687 pluginInfoPB->set_basic_metadata (basicMetadata);
2688
2689 FILE *pbFile = fopen (pbFilePath, "wb");
2690 if (pbFile)
2691 {
2692 google::protobuf::io::FileOutputStream
2693 outputStream (fileno (pbFile));
2694 if (pluginPB)
2695 pluginPB->SerializeToZeroCopyStream (&outputStream);
2696 else
2697 pluginBriefPB->SerializeToZeroCopyStream (&outputStream);
2698 outputStream.Close ();
2699 }
2700 }
2701 #endif
2702
2703 /* Returns TRUE on success. */
2704 static Bool
loadPluginFromXML(CCSContext * context,xmlDoc * doc,char * filename,void * pluginInfoPBv)2705 loadPluginFromXML (CCSContext * context,
2706 xmlDoc * doc,
2707 char *filename,
2708 void * pluginInfoPBv)
2709 {
2710 xmlNode **nodes;
2711 int num;
2712 Bool success = FALSE;
2713
2714 nodes = getNodesFromXPath (doc, NULL, "/compiz/core", &num);
2715 if (num)
2716 {
2717 success = addCoreSettingsFromXMLNode (context, nodes[0], filename,
2718 pluginInfoPBv);
2719 free (nodes);
2720 return success;
2721 }
2722
2723 nodes = getNodesFromXPath (doc, NULL, "/compiz/plugin", &num);
2724 if (num)
2725 {
2726 success = addPluginFromXMLNode (context, nodes[0], filename,
2727 pluginInfoPBv);
2728 free (nodes);
2729 }
2730 return success;
2731 }
2732
2733 #ifdef USE_PROTOBUF
2734 static void
updatePBFilePath(CCSContext * context,char * name,char * pbFilePath)2735 updatePBFilePath (CCSContext * context, char *name, char *pbFilePath)
2736 {
2737 CCSPlugin *plugin = ccsFindPlugin (context, name);
2738 if (plugin)
2739 {
2740 PLUGIN_PRIV (plugin);
2741
2742 if (pPrivate->pbFilePath)
2743 free (pPrivate->pbFilePath);
2744 pPrivate->pbFilePath = strdup (pbFilePath);
2745 }
2746 }
2747 #endif
2748
2749 static void
loadPluginFromXMLFile(CCSContext * context,char * xmlName,char * xmlDirPath)2750 loadPluginFromXMLFile (CCSContext * context, char *xmlName, char *xmlDirPath)
2751 {
2752 char *xmlFilePath = NULL;
2753 char *pbFilePath = NULL;
2754 void *pluginInfoPBv = NULL;
2755
2756 asprintf (&xmlFilePath, "%s/%s", xmlDirPath, xmlName);
2757 if (!xmlFilePath)
2758 {
2759 fprintf (stderr, "[ERROR]: Can't allocate memory\n");
2760 return;
2761 }
2762
2763 #ifdef USE_PROTOBUF
2764 char *name = NULL;
2765 struct stat xmlStat;
2766 Bool removePB = FALSE;
2767
2768 if (usingProtobuf)
2769 {
2770 if (stat (xmlFilePath, &xmlStat))
2771 {
2772 free (xmlFilePath);
2773 return;
2774 }
2775
2776 // Check if the corresponding .pb exists in cache
2777 Bool error = TRUE;
2778 struct stat pbStat;
2779
2780 name = strndup (xmlName, strlen (xmlName) - 4);
2781 if (!name)
2782 {
2783 fprintf (stderr, "[ERROR]: Can't allocate memory\n");
2784 free (xmlFilePath);
2785 return;
2786 }
2787
2788 if (createProtoBufCacheDir () &&
2789 metadataCacheDir.length () > 0)
2790 {
2791 asprintf (&pbFilePath, "%s/%s.pb", metadataCacheDir.c_str (), name);
2792 if (!pbFilePath)
2793 {
2794 fprintf (stderr, "[ERROR]: Can't allocate memory\n");
2795 free (xmlFilePath);
2796 free (name);
2797 return;
2798 }
2799 error = stat (pbFilePath, &pbStat);
2800 }
2801
2802 if (!error)
2803 {
2804 if (checkAndLoadProtoBuf (pbFilePath, &pbStat, &xmlStat,
2805 &persistentPluginBriefPB))
2806 {
2807 // Found and loaded .pb
2808 if (!strcmp (name, "core"))
2809 addCoreSettingsFromPB (context,
2810 persistentPluginBriefPB.info (),
2811 pbFilePath, xmlFilePath);
2812 else
2813 addPluginFromPB (context, persistentPluginBriefPB.info (),
2814 pbFilePath, xmlFilePath);
2815
2816 updatePBFilePath (context, name, pbFilePath);
2817
2818 free (xmlFilePath);
2819 free (pbFilePath);
2820 free (name);
2821 return;
2822 }
2823 else
2824 {
2825 removePB = TRUE;
2826 }
2827 }
2828 persistentPluginBriefPB.Clear ();
2829 pluginInfoPBv = persistentPluginBriefPB.mutable_info ();
2830 }
2831 #endif
2832
2833 // Load from .xml
2834 FILE *fp = fopen (xmlFilePath, "r");
2835 Bool xmlLoaded = FALSE;
2836
2837 if (fp)
2838 {
2839 fclose (fp);
2840 xmlDoc *doc = xmlReadFile (xmlFilePath, NULL, 0);
2841 if (doc)
2842 {
2843 xmlLoaded = loadPluginFromXML (context, doc, xmlFilePath,
2844 pluginInfoPBv);
2845 xmlFreeDoc (doc);
2846 }
2847 }
2848 free (xmlFilePath);
2849
2850 #ifdef USE_PROTOBUF
2851 if (usingProtobuf && xmlLoaded)
2852 {
2853 if (removePB)
2854 remove (pbFilePath); // Attempt to remove .pb
2855 writePBFile (pbFilePath, NULL, &persistentPluginBriefPB, &xmlStat);
2856 updatePBFilePath (context, name, pbFilePath);
2857 }
2858
2859 if (pbFilePath)
2860 free (pbFilePath);
2861 if (name)
2862 free (name);
2863 #endif
2864 }
2865
2866 static void
loadPluginsFromXMLFiles(CCSContext * context,char * path)2867 loadPluginsFromXMLFiles (CCSContext * context, char *path)
2868 {
2869 struct dirent **nameList;
2870 int nFile, i;
2871
2872 if (!path)
2873 return;
2874
2875 nFile = scandir (path, &nameList, pluginXMLFilter, NULL);
2876
2877 if (nFile <= 0)
2878 return;
2879
2880 for (i = 0; i < nFile; i++)
2881 {
2882 loadPluginFromXMLFile (context, nameList[i]->d_name, path);
2883 free (nameList[i]);
2884 }
2885 free (nameList);
2886 }
2887
2888 static void
addPluginNamed(CCSContext * context,char * name)2889 addPluginNamed (CCSContext * context, char *name)
2890 {
2891 CCSPlugin *plugin;
2892 CCSPluginPrivate *pPrivate;
2893
2894 if (ccsFindPlugin (context, name))
2895 return;
2896
2897 if (!strcmp (name, "ini") || !strcmp (name, "gconf") ||
2898 !strcmp (name, "ccp") || !strcmp (name, "kconfig"))
2899 return;
2900
2901 plugin = (CCSPlugin *) calloc (1, sizeof (CCSPlugin));
2902 if (!plugin)
2903 return;
2904
2905 pPrivate = (CCSPluginPrivate *) calloc (1, sizeof (CCSPluginPrivate));
2906 if (!pPrivate)
2907 {
2908 free (plugin);
2909 return;
2910 }
2911
2912 plugin->ccsPrivate = (void *) pPrivate;
2913
2914 plugin->context = context;
2915 plugin->name = strdup (name);
2916
2917 if (!plugin->shortDesc)
2918 plugin->shortDesc = strdup (name);
2919 if (!plugin->longDesc)
2920 plugin->longDesc = strdup (name);
2921 if (!plugin->category)
2922 plugin->category = strdup ("");
2923
2924 pPrivate->loaded = TRUE;
2925 collateGroups (pPrivate);
2926 context->plugins = ccsPluginListAppend (context->plugins, plugin);
2927 }
2928
2929 static void
loadPluginsFromName(CCSContext * context,char * path)2930 loadPluginsFromName (CCSContext * context, char *path)
2931 {
2932 struct dirent **nameList;
2933 int nFile, i;
2934
2935 if (!path)
2936 return;
2937
2938 nFile = scandir (path, &nameList, pluginNameFilter, NULL);
2939 if (nFile <= 0)
2940 return;
2941
2942 for (i = 0; i < nFile; i++)
2943 {
2944 char name[1024];
2945 sscanf (nameList[i]->d_name, "lib%s", name);
2946 if (strlen (name) > 3)
2947 name[strlen (name) - 3] = 0;
2948 free (nameList[i]);
2949 addPluginNamed (context, name);
2950 }
2951 free (nameList);
2952 }
2953
2954 #ifdef USE_PROTOBUF
2955 static inline void
initPBLoading()2956 initPBLoading ()
2957 {
2958 // Update usingProtobuf with the COMPIZ_NO_PROTOBUF environment variable
2959 char *compizNoProtobuf = getenv ("COMPIZ_NO_PROTOBUF");
2960 usingProtobuf = !(compizNoProtobuf &&
2961 (strcasecmp (compizNoProtobuf, "1") == 0 ||
2962 strcasecmp (compizNoProtobuf, "yes") == 0 ||
2963 strcasecmp (compizNoProtobuf, "true") == 0));
2964 if (usingProtobuf)
2965 {
2966 // Verify that the version of the library that we linked against is
2967 // compatible with the version of the headers we compiled against.
2968 GOOGLE_PROTOBUF_VERIFY_VERSION;
2969 }
2970 }
2971 #endif
2972
2973 Bool
ccsLoadPlugin(CCSContext * context,char * name)2974 ccsLoadPlugin (CCSContext * context, char *name)
2975 {
2976 #ifdef USE_PROTOBUF
2977 initPBLoading ();
2978 #endif
2979
2980 char *xmlDirPath = NULL;
2981 char *xmlName = NULL;
2982 asprintf (&xmlName, "%s.xml", name);
2983
2984 if (xmlName)
2985 {
2986 char *home = getenv ("HOME");
2987 if (home && strlen (home))
2988 {
2989 char *home = getenv ("HOME");
2990 asprintf (&xmlDirPath, "%s/.compiz/metadata", home);
2991 if (xmlDirPath)
2992 {
2993 loadPluginFromXMLFile (context, xmlName, xmlDirPath);
2994 free (xmlDirPath);
2995 }
2996 }
2997
2998 loadPluginFromXMLFile (context, xmlName, (char *) METADATADIR);
2999 free (xmlName);
3000 }
3001
3002 return (ccsFindPlugin (context, name) != NULL);
3003 }
3004
3005 void
ccsLoadPlugins(CCSContext * context)3006 ccsLoadPlugins (CCSContext * context)
3007 {
3008 D (D_FULL, "Adding plugins\n");
3009
3010 #ifdef USE_PROTOBUF
3011 initPBLoading ();
3012 #endif
3013
3014 char *home = getenv ("HOME");
3015 if (home && strlen (home))
3016 {
3017 char *homeplugins = NULL;
3018 asprintf (&homeplugins, "%s/.compiz/metadata", home);
3019 if (homeplugins)
3020 {
3021 loadPluginsFromXMLFiles (context, homeplugins);
3022 free (homeplugins);
3023 }
3024 }
3025 loadPluginsFromXMLFiles (context, (char *)METADATADIR);
3026
3027 if (home && strlen (home))
3028 {
3029 char *homeplugins = NULL;
3030 asprintf (&homeplugins, "%s/.compiz/plugins", home);
3031 if (homeplugins)
3032 {
3033 loadPluginsFromName (context, homeplugins);
3034 free (homeplugins);
3035 }
3036 }
3037 loadPluginsFromName (context, (char *)PLUGINDIR);
3038 }
3039
3040 static void
loadOptionsStringExtensionsFromXML(CCSPlugin * plugin,void * pluginPBv,struct stat * xmlStat)3041 loadOptionsStringExtensionsFromXML (CCSPlugin * plugin,
3042 void * pluginPBv,
3043 struct stat *xmlStat)
3044 {
3045 PLUGIN_PRIV (plugin);
3046
3047 xmlDoc *doc = NULL;
3048 xmlNode **nodes;
3049 int num;
3050
3051 if (stat (pPrivate->xmlFile, xmlStat))
3052 return;
3053
3054 FILE *fp = fopen (pPrivate->xmlFile, "r");
3055 if (!fp)
3056 return;
3057
3058 fclose (fp);
3059 doc = xmlReadFile (pPrivate->xmlFile, NULL, 0);
3060
3061 nodes = getNodesFromXPath (doc, NULL, pPrivate->xmlPath, &num);
3062 if (num)
3063 {
3064 initOptionsFromRootNode (plugin, nodes[0], pluginPBv);
3065 if (!basicMetadata)
3066 initStringExtensionsFromRootNode (plugin, nodes[0], pluginPBv);
3067 free (nodes);
3068 }
3069 if (doc)
3070 xmlFreeDoc (doc);
3071 }
3072
3073 void
ccsLoadPluginSettings(CCSPlugin * plugin)3074 ccsLoadPluginSettings (CCSPlugin * plugin)
3075 {
3076 Bool ignoreXML = FALSE;
3077 Bool loadedAtLeastBriefPB = FALSE;
3078 void *pluginPBToWrite = NULL;
3079
3080 #ifdef USE_PROTOBUF
3081 initPBLoading ();
3082 #endif
3083
3084 PLUGIN_PRIV (plugin);
3085
3086 if (pPrivate->loaded)
3087 return;
3088
3089 pPrivate->loaded = TRUE;
3090 D (D_FULL, "Initializing %s options...", plugin->name);
3091
3092 #ifdef USE_PROTOBUF
3093 if (usingProtobuf && pPrivate->pbFilePath)
3094 {
3095 loadedAtLeastBriefPB =
3096 loadPluginMetadataFromProtoBuf (pPrivate->pbFilePath,
3097 NULL, &persistentPluginPB);
3098 if (loadedAtLeastBriefPB)
3099 {
3100 if (!persistentPluginPB.info ().brief_metadata () &&
3101 (basicMetadata ||
3102 !persistentPluginPB.info ().basic_metadata ()))
3103 {
3104 initOptionsFromPB (plugin, persistentPluginPB);
3105 if (!basicMetadata)
3106 initStringExtensionsFromPB (plugin, persistentPluginPB);
3107 ignoreXML = TRUE;
3108 }
3109 else
3110 pluginPBToWrite = &persistentPluginPB;
3111 }
3112 else
3113 pluginPBToWrite = &persistentPluginPB;
3114 }
3115 #endif
3116
3117 struct stat xmlStat;
3118
3119 // Load from .xml
3120 if (!ignoreXML && pPrivate->xmlFile)
3121 loadOptionsStringExtensionsFromXML (plugin, pluginPBToWrite, &xmlStat);
3122
3123 #ifdef USE_PROTOBUF
3124 if (pluginPBToWrite && pPrivate->pbFilePath && loadedAtLeastBriefPB)
3125 writePBFile (pPrivate->pbFilePath, (PluginMetadata *) pluginPBToWrite,
3126 NULL, &xmlStat);
3127 #endif
3128 D (D_FULL, "done\n");
3129
3130 collateGroups (pPrivate);
3131 ccsReadPluginSettings (plugin);
3132 }
3133
3134