[pango/simple-fontmap: 5/11] Add PangoFcHbFontMap




commit 7a7f34c2335be3cf2a1efecc8044ae5b3e22d515
Author: Matthias Clasen <mclasen redhat com>
Date:   Mon Nov 1 22:04:14 2021 -0400

    Add PangoFcHbFontMap
    
    This is a new fontmap implementation that works by
    populating a PangoHbFontMap from fontconfig data. It
    relies on the base class to provide caching and lookups.
    
    In contrast to the existing fontconfig fontmap, this
    does not include every font in every fontset.

 pango/meson.build         |   2 +
 pango/pangofc-hbfontmap.c | 625 ++++++++++++++++++++++++++++++++++++++++++++++
 pango/pangofc-hbfontmap.h |  41 +++
 3 files changed, 668 insertions(+)
---
diff --git a/pango/meson.build b/pango/meson.build
index 79bc313f..29fb6f2f 100644
--- a/pango/meson.build
+++ b/pango/meson.build
@@ -207,11 +207,13 @@ if build_pangoft2
     'pangofc-font.h',
     'pangofc-fontmap.h',
     'pangofc-decoder.h',
+    'pangofc-hbfontmap.h',
   ]
 
   pangofc_public_sources = [
     'pangofc-font.c',
     'pangofc-fontmap.c',
+    'pangofc-hbfontmap.c',
     'pangofc-language-set.c',
     'pangofc-decoder.c',
     'pango-trace.c',
diff --git a/pango/pangofc-hbfontmap.c b/pango/pangofc-hbfontmap.c
new file mode 100644
index 00000000..637dd656
--- /dev/null
+++ b/pango/pangofc-hbfontmap.c
@@ -0,0 +1,625 @@
+/* Pango
+ *
+ * Copyright (C) 2021 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include <math.h>
+
+#include <gio/gio.h>
+
+#include "pangofc-hbfontmap.h"
+#include "pango-hbfamily-private.h"
+#include "pango-hbfontmap-private.h"
+#include "pango-hbface-private.h"
+#include "pango-hbfont-private.h"
+#include "pango-context.h"
+#include "pango-impl-utils.h"
+#include "pango-trace-private.h"
+#include "pangofc-language-set-private.h"
+
+#include <fontconfig/fontconfig.h>
+#include <hb-ot.h>
+
+
+/**
+ * PangoFcHbFontMap:
+ *
+ * `PangoFcHbFontMap` is a subclass of `PangoHbFontMap` that uses
+ * fontconfig to populate the fontmap with the available fonts.
+ */
+
+
+/* The maximum number of faces to include in each of the generic aliases */
+
+#define MAX_ALIAS_FACES 30
+
+
+struct _PangoFcHbFontMap
+{
+  PangoHbFontMap parent_instance;
+
+  FcConfig *config;
+};
+
+struct _PangoFcHbFontMapClass
+{
+  PangoHbFontMapClass parent_class;
+};
+
+/* {{{ Utilities */
+
+static PangoHbFace *
+find_face_by_file (PangoFcHbFontMap *self,
+                   const char       *family_name,
+                   const char       *file,
+                   unsigned int      index)
+{
+  PangoHbFamily *family;
+
+  family = PANGO_HB_FAMILY (pango_font_map_get_family (PANGO_FONT_MAP (self), family_name));
+  if (!family)
+    return NULL;
+
+  for (int j = 0; j < family->faces->len; j++)
+    {
+      PangoHbFace *face = g_ptr_array_index (family->faces, j);
+
+      if (face->index == index &&
+          g_strcmp0 (face->file, file) == 0)
+        return face;
+    }
+
+  return NULL;
+}
+
+/* }}} */
+/* {{{ Fontconfig utilities */
+
+static gboolean
+is_supported_font_format (FcPattern *pattern)
+{
+  FcResult res;
+  const char *fontformat;
+  const char *file;
+
+  /* Harfbuzz loads woff fonts, but we don't get any glyphs */
+  res = FcPatternGetString (pattern, FC_FILE, 0, (FcChar8 **)(void*)&file);
+  if (res == FcResultMatch &&
+      (g_str_has_suffix (file, ".woff") || g_str_has_suffix (file, ".woff2")))
+    return FALSE;
+
+  res = FcPatternGetString (pattern, FC_FONTFORMAT, 0, (FcChar8 **)(void*)&fontformat);
+  if (res != FcResultMatch)
+    return FALSE;
+
+  /* Harfbuzz supports only SFNT fonts */
+
+  /* FIXME: "CFF" is used for both CFF in OpenType and bare CFF files, but
+   * HarfBuzz does not support the later and FontConfig does not seem
+   * to have a way to tell them apart.
+   */
+  if (g_ascii_strcasecmp (fontformat, "TrueType") == 0 ||
+      g_ascii_strcasecmp (fontformat, "CFF") == 0)
+    return TRUE;
+
+  return FALSE;
+}
+
+static PangoStyle
+convert_fc_slant_to_pango (int fc_style)
+{
+  switch (fc_style)
+    {
+    case FC_SLANT_ROMAN:
+      return PANGO_STYLE_NORMAL;
+    case FC_SLANT_ITALIC:
+      return PANGO_STYLE_ITALIC;
+    case FC_SLANT_OBLIQUE:
+      return PANGO_STYLE_OBLIQUE;
+    default:
+      return PANGO_STYLE_NORMAL;
+    }
+}
+
+static PangoStretch
+convert_fc_width_to_pango (int fc_stretch)
+{
+  switch (fc_stretch)
+    {
+    case FC_WIDTH_NORMAL:
+      return PANGO_STRETCH_NORMAL;
+    case FC_WIDTH_ULTRACONDENSED:
+      return PANGO_STRETCH_ULTRA_CONDENSED;
+    case FC_WIDTH_EXTRACONDENSED:
+      return PANGO_STRETCH_EXTRA_CONDENSED;
+    case FC_WIDTH_CONDENSED:
+      return PANGO_STRETCH_CONDENSED;
+    case FC_WIDTH_SEMICONDENSED:
+      return PANGO_STRETCH_SEMI_CONDENSED;
+    case FC_WIDTH_SEMIEXPANDED:
+      return PANGO_STRETCH_SEMI_EXPANDED;
+    case FC_WIDTH_EXPANDED:
+      return PANGO_STRETCH_EXPANDED;
+    case FC_WIDTH_EXTRAEXPANDED:
+      return PANGO_STRETCH_EXTRA_EXPANDED;
+    case FC_WIDTH_ULTRAEXPANDED:
+      return PANGO_STRETCH_ULTRA_EXPANDED;
+    default:
+      return PANGO_STRETCH_NORMAL;
+    }
+}
+
+static PangoWeight
+convert_fc_weight_to_pango (double fc_weight)
+{
+  return FcWeightToOpenTypeDouble (fc_weight);
+}
+
+#define PANGO_FC_GRAVITY "gravity"
+
+/* Create font description that matches the pattern.
+ * We explicitly don't want to include variant, gravity,
+ * variations and font features here, since there are
+ * handled on the font level, by PangoHbFont, not
+ * by PangoHbFace.
+ */
+static PangoFontDescription *
+pango_font_description_from_pattern (FcPattern *pattern)
+{
+  PangoFontDescription *desc;
+  PangoStyle style;
+  PangoWeight weight;
+  PangoStretch stretch;
+  FcChar8 *s;
+  int i;
+  double d;
+  FcResult res;
+
+  desc = pango_font_description_new ();
+
+  res = FcPatternGetString (pattern, FC_FAMILY, 0, (FcChar8 **) &s);
+  g_assert (res == FcResultMatch);
+
+  pango_font_description_set_family (desc, (gchar *)s);
+
+  if (FcPatternGetInteger (pattern, FC_SLANT, 0, &i) == FcResultMatch)
+    style = convert_fc_slant_to_pango (i);
+  else
+    style = PANGO_STYLE_NORMAL;
+
+  pango_font_description_set_style (desc, style);
+
+  if (FcPatternGetDouble (pattern, FC_WEIGHT, 0, &d) == FcResultMatch)
+    weight = convert_fc_weight_to_pango (d);
+  else
+    weight = PANGO_WEIGHT_NORMAL;
+
+  pango_font_description_set_weight (desc, weight);
+
+  if (FcPatternGetInteger (pattern, FC_WIDTH, 0, &i) == FcResultMatch)
+    stretch = convert_fc_width_to_pango (i);
+  else
+    stretch = PANGO_STRETCH_NORMAL;
+
+  pango_font_description_set_stretch (desc, stretch);
+
+  return desc;
+}
+
+static const char *
+style_name_from_pattern (FcPattern *pattern)
+{
+  const char *font_style;
+  int weight, slant;
+
+  if (FcPatternGetString (pattern, FC_STYLE, 0, (FcChar8 **)(void*)&font_style) == FcResultMatch)
+    return font_style;
+
+  if (FcPatternGetInteger(pattern, FC_WEIGHT, 0, &weight) != FcResultMatch)
+    weight = FC_WEIGHT_MEDIUM;
+
+  if (FcPatternGetInteger(pattern, FC_SLANT, 0, &slant) != FcResultMatch)
+    slant = FC_SLANT_ROMAN;
+
+  if (weight <= FC_WEIGHT_MEDIUM)
+    {
+      if (slant == FC_SLANT_ROMAN)
+        return "Regular";
+      else
+        return "Italic";
+    }
+  else
+    {
+      if (slant == FC_SLANT_ROMAN)
+        return "Bold";
+      else
+        return "Bold Italic";
+    }
+
+  return "Normal";
+}
+
+static gboolean
+font_matrix_from_pattern (FcPattern   *pattern,
+                          PangoMatrix *matrix)
+{
+  FcMatrix fc_matrix, *fc_matrix_val;
+  int i;
+
+  FcMatrixInit (&fc_matrix);
+  for (i = 0; FcPatternGetMatrix (pattern, FC_MATRIX, i, &fc_matrix_val) == FcResultMatch; i++)
+    FcMatrixMultiply (&fc_matrix, &fc_matrix, fc_matrix_val);
+
+  if (i > 0)
+    {
+      matrix->xx = fc_matrix.xx;
+      matrix->yx = fc_matrix.yx;
+      matrix->xy = - fc_matrix.xy;
+      matrix->yy = - fc_matrix.yy;
+
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+static PangoHbFace *
+pango_hb_face_from_pattern (PangoFcHbFontMap *self,
+                            FcPattern        *pattern,
+                            GHashTable       *lang_hash)
+{
+  const char *family_name, *file;
+  int index;
+  int instance_id;
+  PangoFontDescription *description;
+  const char *name;
+  PangoHbFace *orig;
+  PangoHbFace *face;
+  PangoMatrix font_matrix;
+  FcLangSet *langs;
+  gboolean variable;
+
+  if (!is_supported_font_format (pattern))
+    return NULL;
+
+  if (FcPatternGetString (pattern, FC_FAMILY, 0, (FcChar8 **)(void*)&family_name) != FcResultMatch)
+    return NULL;
+
+  if (FcPatternGetString (pattern, FC_FILE, 0, (FcChar8 **)(void*)&file) != FcResultMatch)
+    return NULL;
+
+  if (FcPatternGetInteger (pattern, FC_INDEX, 0, &index) != FcResultMatch)
+    index = 0;
+
+  if (FcPatternGetBool (pattern, FC_VARIABLE, 0, &variable) != FcResultMatch)
+    variable = FALSE;
+
+  instance_id = (index >> 16) - 1;
+  index = index & 0xffff;
+
+  if (variable)
+    instance_id = -2;
+
+  description = pango_font_description_from_pattern (pattern);
+  name = style_name_from_pattern (pattern);
+
+  orig = find_face_by_file (self, family_name, file, index);
+  if (orig)
+    face = pango_hb_face_new_variant (orig, instance_id, NULL, FALSE, description);
+  else
+    face = pango_hb_face_new_from_file (file, index, instance_id, name, description);
+
+  pango_font_description_free (description);
+
+  if (font_matrix_from_pattern (pattern, &font_matrix))
+    pango_hb_face_set_matrix (face, &font_matrix);
+
+  if (FcPatternGetLangSet (pattern, FC_LANG, 0, &langs) == FcResultMatch)
+    {
+      PangoFcLanguageSet lookup;
+      PangoLanguageSet *languages;
+
+      lookup.langs = langs;
+      languages = g_hash_table_lookup (lang_hash, &lookup);
+      if (!languages)
+        {
+          languages = PANGO_LANGUAGE_SET (pango_fc_language_set_new (langs));
+          g_hash_table_add (lang_hash, languages);
+        }
+      pango_hb_face_set_language_set (face, languages);
+    }
+
+  return face;
+}
+
+static void
+pango_fc_hb_font_map_populate (PangoHbFontMap *map)
+{
+  PangoFcHbFontMap *self = PANGO_FC_HB_FONT_MAP (map);
+  FcPattern *pat;
+  FcFontSet *fontset;
+  int k;
+  GHashTable *lang_hash;
+  FcObjectSet *os;
+  gint64 before G_GNUC_UNUSED;
+
+  before = PANGO_TRACE_CURRENT_TIME;
+
+  os = FcObjectSetBuild (FC_FAMILY, FC_SPACING, FC_STYLE, FC_WEIGHT,
+                         FC_WIDTH, FC_SLANT, FC_VARIABLE, FC_FONTFORMAT,
+                         FC_FILE, FC_INDEX, FC_LANG, NULL);
+
+  pat = FcPatternCreate ();
+  fontset = FcFontList (self->config, pat, os);
+  FcPatternDestroy (pat);
+
+  lang_hash = g_hash_table_new_full ((GHashFunc) pango_fc_language_set_hash,
+                                     (GEqualFunc) pango_fc_language_set_equal,
+                                     NULL, g_object_unref);
+
+  for (k = 0; k < fontset->nfont; k++)
+    {
+      PangoHbFace *face = pango_hb_face_from_pattern (self, fontset->fonts[k], lang_hash);
+      if (!face)
+        continue;
+
+      pango_hb_font_map_add_face (PANGO_HB_FONT_MAP (self), face);
+    }
+
+  g_hash_table_unref (lang_hash);
+
+  FcFontSetDestroy (fontset);
+
+  /* Add aliases */
+  const char *alias_names[] = {
+    "system-ui",
+    "emoji"
+  };
+
+  for (int i = 0; i < G_N_ELEMENTS (alias_names); i++)
+    {
+      FcPattern *pattern;
+      FcPattern *ret;
+      FcResult res;
+      const char *family_name;
+
+      if (pango_font_map_get_family (PANGO_FONT_MAP (self), alias_names[i]))
+        continue;
+
+      pattern = FcPatternCreate ();
+      FcPatternAddString (pattern, FC_FAMILY, (FcChar8 *) alias_names[i]);
+
+      FcConfigSubstitute (self->config, pattern, FcMatchPattern);
+      FcDefaultSubstitute (pattern);
+
+      ret = FcFontMatch (self->config, pattern, &res);
+
+      if (FcPatternGetString (ret, FC_FAMILY, 0, (FcChar8 **)(void*)&family_name) == FcResultMatch)
+        {
+          PangoHbFamily *family = PANGO_HB_FAMILY (pango_font_map_get_family (PANGO_FONT_MAP (self), 
family_name));
+          if (family)
+            {
+              PangoHbFamily *alias_family = pango_hb_family_new_alias (family, alias_names[i]);
+              pango_hb_font_map_add_family (PANGO_HB_FONT_MAP (self), alias_family);
+            }
+        }
+
+      FcPatternDestroy (ret);
+      FcPatternDestroy (pattern);
+    }
+
+  /* Add generic aliases */
+  const char *generic_names[] = {
+    "monospace",
+    "sans-serif",
+    "serif",
+    "cursive",
+    "fantasy",
+  };
+  FcLangSet *no_langs = FcLangSetCreate ();
+  FcLangSet *emoji_langs = FcLangSetCreate ();
+
+  FcLangSetAdd (emoji_langs, (FcChar8 *)"und-zsye");
+
+  for (int i = 0; i < G_N_ELEMENTS (generic_names); i++)
+    {
+      FcPattern *pattern;
+      FcFontSet *ret;
+      FcResult res;
+      int n_faces;
+
+      if (pango_font_map_get_family (PANGO_FONT_MAP (self), generic_names[i]))
+        continue;
+
+      pango_hb_font_map_add_generic_family (PANGO_HB_FONT_MAP (self), generic_names[i]);
+
+      pattern = FcPatternCreate ();
+      FcPatternAddString (pattern, FC_FAMILY, (FcChar8 *) generic_names[i]);
+
+      FcConfigSubstitute (self->config, pattern, FcMatchPattern);
+      FcDefaultSubstitute (pattern);
+
+      n_faces = 0;
+      ret = FcFontSort (self->config, pattern, TRUE, NULL, &res);
+      for (int j = 0; j < ret->nfont; j++)
+        {
+          PangoHbFace *face;
+          const char *file;
+          int index;
+          PangoFontDescription *desc;
+          FcLangSet *langs;
+          int spacing;
+          const char *family_name;
+
+          pat = ret->fonts[j];
+
+          if (!is_supported_font_format (pat))
+            continue;
+
+          if (FcPatternGetLangSet (pat, FC_LANG, 0, &langs) != FcResultMatch)
+            continue;
+
+          if (FcLangSetEqual (langs, no_langs))
+            continue;
+
+          if ((strcmp (generic_names[i], "emoji") == 0) != FcLangSetEqual (langs, emoji_langs))
+            continue;
+
+          if (FcPatternGetInteger (pat, FC_SPACING, 0, &spacing) != FcResultMatch)
+            spacing = FC_PROPORTIONAL;
+
+          if (strcmp (generic_names[i], "monospace") == 0 && spacing != FC_MONO)
+            continue;
+
+          if (FcPatternGetString (pat, FC_FAMILY, 0, (FcChar8 **)(void*)&family_name) != FcResultMatch)
+            continue;
+
+          if (FcPatternGetString (pat, FC_FILE, 0, (FcChar8 **)(void*)&file) != FcResultMatch)
+            continue;
+
+          if (FcPatternGetInteger (pat, FC_INDEX, 0, &index) != FcResultMatch)
+            index = 0;
+
+          index = index & 0xffff;
+
+          face = find_face_by_file (self, family_name, file, index);
+          if (!face)
+            continue;
+
+          desc = pango_font_description_new ();
+          pango_font_description_set_family (desc, generic_names[i]);
+          pango_hb_font_map_add_face (PANGO_HB_FONT_MAP (self),
+                                      pango_hb_face_new_variant (PANGO_HB_FACE (face),
+                                                                 0xffff,
+                                                                 NULL,
+                                                                 FALSE,
+                                                                 desc));
+          pango_font_description_free (desc);
+
+          n_faces++;
+          if (n_faces == MAX_ALIAS_FACES)
+            break;
+       }
+
+      FcFontSetDestroy (ret);
+      FcPatternDestroy (pattern);
+    }
+
+  FcLangSetDestroy (no_langs);
+  FcLangSetDestroy (emoji_langs);
+
+  FcObjectSetDestroy (os);
+
+  pango_trace_mark (before, "populate FcHbFontMap", NULL);
+}
+
+/* }}} */
+/* {{{ PangoFontMap implementation */
+
+G_DEFINE_TYPE (PangoFcHbFontMap, pango_fc_hb_font_map, PANGO_TYPE_HB_FONT_MAP)
+
+static void
+pango_fc_hb_font_map_init (PangoFcHbFontMap *self)
+{
+  pango_hb_font_map_repopulate (PANGO_HB_FONT_MAP (self), TRUE);
+}
+
+static void
+pango_fc_hb_font_map_finalize (GObject *object)
+{
+  PangoFcHbFontMap *self = PANGO_FC_HB_FONT_MAP (object);
+
+  if (self->config)
+    FcConfigDestroy (self->config);
+
+  G_OBJECT_CLASS (pango_fc_hb_font_map_parent_class)->finalize (object);
+}
+
+static void
+pango_fc_hb_font_map_class_init (PangoFcHbFontMapClass *class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+  PangoHbFontMapClass *hb_font_map_class = PANGO_HB_FONT_MAP_CLASS (class);
+  gint64 before G_GNUC_UNUSED;
+
+  before = PANGO_TRACE_CURRENT_TIME;
+
+  FcInit ();
+
+  pango_trace_mark (before, "FcInit", NULL);
+
+  object_class->finalize = pango_fc_hb_font_map_finalize;
+
+  hb_font_map_class->populate = pango_fc_hb_font_map_populate;
+}
+
+/* }}} */
+/* {{{ Public API */
+
+/**
+ * pango_fc_hb_font_map_new:
+ *
+ * Creates a new `PangoFcHbFontMap` object.
+ *
+ * Unless overridden by [method@PangoFc.HbFontMap.set_config],
+ * this font map uses the default fontconfig configuration.
+ *
+ * Returns: a new `PangoFcHbFontMap`
+ *
+ * Since: 1.52
+ */
+PangoFcHbFontMap *
+pango_fc_hb_font_map_new (void)
+{
+  return g_object_new (PANGO_TYPE_FC_HB_FONT_MAP, NULL);
+}
+
+/**
+ * pango_fc_hb_font_map_set_config: (skip)
+ * @self: a `PangoFcHbFontMap`
+ * @config: (nullable): the `FcConfig` to use, or `NULL` to use
+ *   the default configuration
+ *
+ * Sets the fontconfig configuration to use.
+ *
+ * Note that changing the fontconfig configuration
+ * removes all cached font families, faces and fonts.
+ *
+ * Since: 1.52
+ */
+void
+pango_fc_hb_font_map_set_config (PangoFcHbFontMap *self,
+                                 FcConfig         *config)
+{
+  g_return_if_fail (PANGO_IS_FC_HB_FONT_MAP (self));
+
+  if (self->config)
+    FcConfigDestroy (self->config);
+
+  self->config = config;
+
+  if (self->config)
+    FcConfigReference (self->config);
+
+  pango_hb_font_map_repopulate (PANGO_HB_FONT_MAP (self), TRUE);
+}
+
+/* }}} */
+
+/* vim:set foldmethod=marker expandtab: */
diff --git a/pango/pangofc-hbfontmap.h b/pango/pangofc-hbfontmap.h
new file mode 100644
index 00000000..a9a5a66d
--- /dev/null
+++ b/pango/pangofc-hbfontmap.h
@@ -0,0 +1,41 @@
+/* Pango
+ * pangofc-hbfontmap.h: Fontmap for fontconfig
+ *
+ * Copyright (C) 2021 Red Hat, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.         See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#pragma once
+
+#include <pango/pango.h>
+#include <fontconfig/fontconfig.h>
+
+G_BEGIN_DECLS
+
+#define PANGO_TYPE_FC_HB_FONT_MAP      (pango_fc_hb_font_map_get_type ())
+
+PANGO_AVAILABLE_IN_1_52
+PANGO_DECLARE_INTERNAL_TYPE (PangoFcHbFontMap, pango_fc_hb_font_map, PANGO, FC_HB_FONT_MAP, PangoHbFontMap)
+
+PANGO_AVAILABLE_IN_1_52
+PangoFcHbFontMap *      pango_fc_hb_font_map_new         (void);
+
+PANGO_AVAILABLE_IN_1_52
+void                    pango_fc_hb_font_map_set_config  (PangoFcHbFontMap *self,
+                                                          FcConfig         *config);
+
+G_END_DECLS


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]