3506
find_version (const char **attribute_names,
3507
const char **attribute_values)
3511
for (i = 0; attribute_names[i]; i++)
3513
if (strcmp (attribute_names[i], "version") == 0)
3514
return attribute_values[i];
3520
/* Returns whether the version element was successfully parsed.
3521
* If successfully parsed, then two additional items are returned:
3523
* satisfied: whether this version of Mutter meets the version check
3524
* minimum_required: minimum version of theme format required by version check
3527
check_version (GMarkupParseContext *context,
3528
const char *version_str,
3529
gboolean *satisfied,
3530
guint *minimum_required,
3533
static GRegex *version_regex;
3535
char *comparison_str, *major_str, *minor_str;
3538
*minimum_required = 0;
3541
version_regex = g_regex_new ("^\\s*([<>]=?)\\s*(\\d+)(\\.\\d+)?\\s*$", 0, 0, NULL);
3543
if (!g_regex_match (version_regex, version_str, 0, &info))
3545
g_match_info_free (info);
3546
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
3547
_("Bad version specification '%s'"), version_str);
3551
comparison_str = g_match_info_fetch (info, 1);
3552
major_str = g_match_info_fetch (info, 2);
3553
minor_str = g_match_info_fetch (info, 3);
3555
version = 1000 * atoi (major_str);
3556
/* might get NULL, see: https://bugzilla.gnome.org/review?bug=588217 */
3557
if (minor_str && minor_str[0])
3558
version += atoi (minor_str + 1);
3560
if (comparison_str[0] == '<')
3562
if (comparison_str[1] == '=')
3563
*satisfied = THEME_VERSION <= version;
3565
*satisfied = THEME_VERSION < version;
3569
if (comparison_str[1] == '=')
3571
*satisfied = THEME_VERSION >= version;
3572
*minimum_required = version;
3576
*satisfied = THEME_VERSION > version;
3577
*minimum_required = version + 1;
3581
g_free (comparison_str);
3584
g_match_info_free (info);
3414
3590
start_element_handler (GMarkupParseContext *context,
3419
3595
GError **error)
3421
3597
ParseInfo *info = user_data;
3598
const char *version;
3599
guint required_version = 0;
3601
if (info->skip_level > 0)
3607
required_version = peek_required_version (info);
3609
version = find_version (attribute_names, attribute_values);
3610
if (version != NULL)
3613
guint element_required;
3615
if (required_version < 3000)
3617
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
3618
_("\"version\" attribute cannot be used in metacity-theme-1.xml or metacity-theme-2.xml"));
3622
if (!check_version (context, version, &satisfied, &element_required, error))
3625
/* Two different ways of handling an unsatisfied version check:
3626
* for the toplevel element of a file, we throw an error back so
3627
* that the controlling code can go ahead and look for an
3628
* alternate metacity-theme-1.xml or metacity-theme-2.xml; for
3629
* other elements we just silently skip the element and children.
3631
if (peek_state (info) == STATE_START)
3635
if (element_required > info->format_version)
3636
info->format_version = element_required;
3640
set_error (error, context, THEME_PARSE_ERROR, THEME_PARSE_ERROR_TOO_OLD,
3641
_("Theme requires version %s but latest supported theme version is %d.%d"),
3642
version, THEME_VERSION, THEME_MINOR_VERSION);
3646
else if (!satisfied)
3648
info->skip_level = 1;
3652
if (element_required > required_version)
3653
required_version = element_required;
3656
push_required_version (info, required_version);
3423
3658
switch (peek_state (info))
4060
/* We were intending to put the version number
4061
* in the subdirectory name, but we ended up
4062
* using the filename instead. The "-1" survives
4065
#define THEME_SUBDIR "metacity-1"
4067
/* Highest version of the theme format to
4070
#define THEME_VERSION 2
4072
#define METACITY_THEME_FILENAME_FORMAT "metacity-theme-%d.xml"
4075
meta_theme_load (const char *theme_name,
4306
/* If the theme is not-corrupt, keep looking for alternate versions
4307
* in other locations we might be compatible with
4310
theme_error_is_fatal (GError *error)
4312
return !(error->domain == G_FILE_ERROR ||
4313
(error->domain == THEME_PARSE_ERROR &&
4314
error->code == THEME_PARSE_ERROR_TOO_OLD));
4318
load_theme (const char *theme_dir,
4319
const char *theme_name,
4320
guint major_version,
4078
4323
GMarkupParseContext *context;
4080
4324
ParseInfo info;
4327
char *theme_filename;
4083
4328
char *theme_file;
4331
g_return_val_if_fail (error && *error == NULL, NULL);
4337
theme_filename = g_strdup_printf (METACITY_THEME_FILENAME_FORMAT, major_version);
4338
theme_file = g_build_filename (theme_dir, theme_filename, NULL);
4340
if (!g_file_get_contents (theme_file, &text, &length, error))
4343
meta_topic (META_DEBUG_THEMES, "Parsing theme file %s\n", theme_file);
4345
parse_info_init (&info);
4347
info.theme_name = theme_name;
4348
info.theme_file = theme_file;
4349
info.theme_dir = theme_dir;
4351
info.format_version = 1000 * major_version;
4353
context = g_markup_parse_context_new (&metacity_theme_parser, 0, &info, NULL);
4355
if (!g_markup_parse_context_parse (context, text, length, error))
4358
if (!g_markup_parse_context_end_parse (context, error))
4361
retval = info.theme;
4365
if (*error && !theme_error_is_fatal (*error))
4366
meta_topic (META_DEBUG_THEMES, "Failed to read theme from file %s: %s\n",
4367
theme_file, (*error)->message);
4369
g_free (theme_filename);
4370
g_free (theme_file);
4375
g_markup_parse_context_free (context);
4376
parse_info_free (&info);
4383
keep_trying (GError **error)
4385
if (*error && !theme_error_is_fatal (*error))
4387
g_clear_error (error);
4395
meta_theme_load (const char *theme_name,
4398
GError *error = NULL;
4084
4399
char *theme_dir;
4085
4400
MetaTheme *retval;
4087
4401
const gchar* const* xdg_data_dirs;
4098
4407
if (meta_is_debugging ())
4100
gchar *theme_filename = g_strdup_printf (METACITY_THEME_FILENAME_FORMAT,
4103
/* Try in themes in our source tree */
4104
theme_dir = g_build_filename ("./themes", theme_name, NULL);
4106
theme_file = g_build_filename (theme_dir,
4111
if (!g_file_get_contents (theme_file,
4409
/* We try all supported major versions from current to oldest */
4410
for (major_version = THEME_MAJOR_VERSION; (major_version > 0); major_version--)
4116
meta_topic (META_DEBUG_THEMES, "Failed to read theme from file %s: %s\n",
4117
theme_file, error->message);
4118
g_error_free (error);
4120
g_free (theme_file);
4412
theme_dir = g_build_filename ("./themes", theme_name, NULL);
4413
retval = load_theme (theme_dir, theme_name, major_version, &error);
4415
if (!keep_trying (&error))
4123
version = THEME_VERSION;
4125
g_free (theme_filename);
4128
/* We try all supported versions from current to oldest */
4129
for (version = THEME_VERSION; (version > 0) && (text == NULL); version--)
4420
/* We try all supported major versions from current to oldest */
4421
for (major_version = THEME_MAJOR_VERSION; (major_version > 0); major_version--)
4131
gchar *theme_filename = g_strdup_printf (METACITY_THEME_FILENAME_FORMAT,
4134
/* We try first in home dir, XDG_DATA_DIRS, then system dir for themes */
4423
/* We try first in XDG_USER_DATA_DIR, XDG_DATA_DIRS, then system dir for themes */
4136
/* Try home dir for themes */
4137
theme_dir = g_build_filename (g_get_home_dir (),
4425
/* Try XDG_USER_DATA_DIR first */
4426
theme_dir = g_build_filename (g_get_user_data_dir(),
4143
theme_file = g_build_filename (theme_dir,
4148
if (!g_file_get_contents (theme_file,
4153
meta_topic (META_DEBUG_THEMES, "Failed to read theme from file %s: %s\n",
4154
theme_file, error->message);
4155
g_error_free (error);
4157
g_free (theme_file);
4432
retval = load_theme (theme_dir, theme_name, major_version, &error);
4434
if (!keep_trying (&error))
4161
4437
/* Try each XDG_DATA_DIRS for theme */
4162
4438
xdg_data_dirs = g_get_system_data_dirs();
4163
4439
for(i = 0; xdg_data_dirs[i] != NULL; i++)
4167
theme_dir = g_build_filename (xdg_data_dirs[i],
4173
theme_file = g_build_filename (theme_dir,
4178
if (!g_file_get_contents (theme_file,
4183
meta_topic (META_DEBUG_THEMES, "Failed to read theme from file %s: %s\n",
4184
theme_file, error->message);
4185
g_error_free (error);
4187
g_free (theme_file);
4197
/* Look for themes in METACITY_DATADIR */
4200
theme_dir = g_build_filename (METACITY_DATADIR,
4441
theme_dir = g_build_filename (xdg_data_dirs[i],
4206
theme_file = g_build_filename (theme_dir,
4211
if (!g_file_get_contents (theme_file,
4216
meta_topic (META_DEBUG_THEMES, "Failed to read theme from file %s: %s\n",
4217
theme_file, error->message);
4218
g_error_free (error);
4220
g_free (theme_file);
4447
retval = load_theme (theme_dir, theme_name, major_version, &error);
4449
if (!keep_trying (&error))
4225
g_free (theme_filename);
4230
g_set_error (err, META_THEME_ERROR, META_THEME_ERROR_FAILED,
4231
_("Failed to find a valid file for theme %s\n"),
4234
return NULL; /* all fallbacks failed */
4237
meta_topic (META_DEBUG_THEMES, "Parsing theme file %s\n", theme_file);
4240
parse_info_init (&info);
4241
info.theme_name = theme_name;
4243
/* pass ownership to info so we free it with the info */
4244
info.theme_file = theme_file;
4245
info.theme_dir = theme_dir;
4247
info.format_version = version + 1;
4249
context = g_markup_parse_context_new (&metacity_theme_parser,
4253
if (!g_markup_parse_context_parse (context,
4260
if (!g_markup_parse_context_end_parse (context, &error))
4453
/* Look for themes in METACITY_DATADIR */
4454
theme_dir = g_build_filename (METACITY_DATADIR,
4459
retval = load_theme (theme_dir, theme_name, major_version, &error);
4461
if (!keep_trying (&error))
4268
g_markup_parse_context_free (context);
4272
info.theme->format_version = info.format_version;
4467
if (!error && !retval)
4468
g_set_error (&error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
4469
_("Failed to find a valid file for theme %s\n"),
4276
4474
g_propagate_error (err, error);
4278
else if (info.theme)
4280
/* Steal theme from info */
4281
retval = info.theme;
4286
g_set_error (err, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
4287
_("Theme file %s did not contain a root <metacity_theme> element"),
4291
parse_info_free (&info);