<commit_before>/*
 * Clutter.
 *
 * An OpenGL based 'interactive canvas' library.
 *
 * Authored By <NAME>  <<EMAIL>>
 *
 * Copyright (C) 2006 OpenedHand
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
 *
 *
 */
/**
 * SECTION:clutter-main
 * @short_description: Various 'global' clutter functions.
 *
 * Functions to retrieve various global Clutter resources and other utility
 * functions for mainloops, events and threads
 *
 * <refsect2 id="clutter-Threading-Model">
 *   <title>Threading Model</title>
 *   <para>Clutter is <emphasis>thread-aware</emphasis>: all operations
 *   performed by Clutter are assumed to be under the big Clutter lock,
 *   which is created when the threading is initialized through
 *   clutter_threads_init().</para>
 *   <example id="example-Thread-Init">
 *     <title>Thread Initialization</title>
 *     <para>The code below shows how to correctly initialize Clutter
 *     in a multi-threaded environment. These operations are mandatory for
 *     applications that wish to use threads with Clutter.</para>
 *     <programlisting>
 * int
 * main (int argc, char *argv[])
 * {
 *   /&ast; initialize GLib's threading support &ast;/
 *   g_thread_init (NULL);
 *
 *   /&ast; initialize Clutter's threading support &ast;/
 *   clutter_threads_init ();
 *
 *   /&ast; initialize Clutter &ast;/
 *   clutter_init (&amp;argc, &amp;argv);
 *
 *   /&ast; program code &ast;/
 *
 *   /&ast; acquire the main lock &ast;/
 *   clutter_threads_enter ();
 *
 *   /&ast; start the main loop &ast;/
 *   clutter_main ();
 *
 *   /&ast; release the main lock &ast;/
 *   clutter_threads_leave ();
 *
 *   /&ast; clean up &ast;/
 *   return 0;
 * }
 *     </programlisting>
 *   </example>
 *   <para>This threading model has the caveat that it is only safe to call
 *   Clutter's API when the lock has been acquired &mdash; which happens
 *   between pairs of clutter_threads_enter() and clutter_threads_leave()
 *   calls.</para>
 *   <para>The only safe and portable way to use the Clutter API in a
 *   multi-threaded environment is to never access the API from a thread that
 *   did not call clutter_init() and clutter_main().</para>
 *   <para>The common pattern for using threads with Clutter is to use worker
 *   threads to perform blocking operations and then install idle or timeout
 *   sources with the result when the thread finished.</para>
 *   <para>Clutter provides thread-aware variants of g_idle_add() and
 *   g_timeout_add() that acquire the Clutter lock before invoking the provided
 *   callback: clutter_threads_add_idle() and
 *   clutter_threads_add_timeout().</para>
 *   <para>The example below shows how to use a worker thread to perform a
 *   blocking operation, and perform UI updates using the main loop.</para>
 *   <example id="worker-thread-example">
 *     <title>A worker thread example</title>
 *     <programlisting>
 * <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" parse="text" href="../../../../tests/interactive/test-thread.c">
 *   <xi:fallback>FIXME: MISSING XINCLUDE CONTENT</xi:fallback>
 * </xi:include>
 *     </programlisting>
 *   </example>
 * </refsect2>
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <stdlib.h>
#include <glib/gi18n-lib.h>
#include <locale.h>

#include "clutter-actor.h"
#include "clutter-backend-private.h"
#include "clutter-debug.h"
#include "clutter-device-manager-private.h"
#include "clutter-event.h"
#include "clutter-feature.h"
#include "clutter-frame-source.h"
#include "clutter-main.h"
#include "clutter-master-clock.h"
#include "clutter-private.h"
#include "clutter-profile.h"
#include "clutter-settings-private.h"
#include "clutter-stage-manager.h"
#include "clutter-stage-private.h"
#include "clutter-version.h" 	/* For flavour define */

#include <cogl/cogl.h>
#include <cogl-pango/cogl-pango.h>

#include "cally.h" /* For accessibility support */

/* main context */
static ClutterMainContext *ClutterCntx       = NULL;
G_LOCK_DEFINE_STATIC (ClutterCntx);

/* main lock and locking/unlocking functions */
static GMutex clutter_threads_mutex;
static GCallback clutter_threads_lock        = NULL;
static GCallback clutter_threads_unlock      = NULL;

/* command line options */
static gboolean clutter_is_initialized       = FALSE;
static gboolean clutter_show_fps             = FALSE;
static gboolean clutter_fatal_warnings       = FALSE;
static gboolean clutter_disable_mipmap_text  = FALSE;
static gboolean clutter_use_fuzzy_picking    = FALSE;
static gboolean clutter_enable_accessibility = TRUE;

static guint clutter_default_fps             = 60;

static ClutterTextDirection clutter_text_direction = CLUTTER_TEXT_DIRECTION_LTR;

static guint clutter_main_loop_level         = 0;
static GSList *main_loops                    = NULL;

/* debug flags */
guint clutter_debug_flags       = 0;
guint clutter_paint_debug_flags = 0;
guint clutter_pick_debug_flags  = 0;

/* profile flags */
guint clutter_profile_flags     = 0;

const guint clutter_major_version = CLUTTER_MAJOR_VERSION;
const guint clutter_minor_version = CLUTTER_MINOR_VERSION;
const guint clutter_micro_version = CLUTTER_MICRO_VERSION;

#ifdef CLUTTER_ENABLE_DEBUG
static const GDebugKey clutter_debug_keys[] = {
  { "misc", CLUTTER_DEBUG_MISC },
  { "actor", CLUTTER_DEBUG_ACTOR },
  { "texture", CLUTTER_DEBUG_TEXTURE },
  { "event", CLUTTER_DEBUG_EVENT },
  { "paint", CLUTTER_DEBUG_PAINT },
  { "pick", CLUTTER_DEBUG_PICK },
  { "pango", CLUTTER_DEBUG_PANGO },
  { "backend", CLUTTER_DEBUG_BACKEND },
  { "scheduler", CLUTTER_DEBUG_SCHEDULER },
  { "script", CLUTTER_DEBUG_SCRIPT },
  { "shader", CLUTTER_DEBUG_SHADER },
  { "animation", CLUTTER_DEBUG_ANIMATION },
  { "layout", CLUTTER_DEBUG_LAYOUT },
  { "clipping", CLUTTER_DEBUG_CLIPPING },
  { "oob-transforms", CLUTTER_DEBUG_OOB_TRANSFORMS },
};
#endif /* CLUTTER_ENABLE_DEBUG */

static const GDebugKey clutter_pick_debug_keys[] = {
  { "nop-picking", CLUTTER_DEBUG_NOP_PICKING },
  { "dump-pick-buffers", CLUTTER_DEBUG_DUMP_PICK_BUFFERS },
};

static const GDebugKey clutter_paint_debug_keys[] = {
  { "disable-swap-events", CLUTTER_DEBUG_DISABLE_SWAP_EVENTS },
  { "disable-clipped-redraws", CLUTTER_DEBUG_DISABLE_CLIPPED_REDRAWS },
  { "redraws", CLUTTER_DEBUG_REDRAWS },
  { "paint-volumes", CLUTTER_DEBUG_PAINT_VOLUMES },
  { "disable-culling", CLUTTER_DEBUG_DISABLE_CULLING },
  { "disable-offscreen-redirect", CLUTTER_DEBUG_DISABLE_OFFSCREEN_REDIRECT },
  { "continuous-redraw", CLUTTER_DEBUG_CONTINUOUS_REDRAW },
  { "paint-deform-tiles", CLUTTER_DEBUG_PAINT_DEFORM_TILES },
};

#ifdef CLUTTER_ENABLE_PROFILE
static const GDebugKey clutter_profile_keys[] = {
  {"picking-only", CLUTTER_PROFILE_PICKING_ONLY },
  {"disable-report", CLUTTER_PROFILE_DISABLE_REPORT }
};
#endif /* CLUTTER_ENABLE_DEBUG */

static void
clutter_threads_impl_lock (void)
{
  g_mutex_lock (&clutter_threads_mutex);
}

static void
clutter_threads_impl_unlock (void)
{
  g_mutex_unlock (&clutter_threads_mutex);
}

static inline void
clutter_threads_init_default (void)
{
  g_mutex_init (&clutter_threads_mutex);

  if (clutter_threads_lock == NULL)
    clutter_threads_lock = clutter_threads_impl_lock;

  if (clutter_threads_unlock == NULL)
    clutter_threads_unlock = clutter_threads_impl_unlock;
}

#define ENVIRONMENT_GROUP       "Environment"
#define DEBUG_GROUP             "Debug"

static void
clutter_config_read_from_key_file (GKeyFile *keyfile)
{
  GError *key_error = NULL;
  gboolean bool_value;
  gint int_value;
  gchar *str_value;

  if (!g_key_file_has_group (keyfile, ENVIRONMENT_GROUP))
    return;

  bool_value =
    g_key_file_get_boolean (keyfile, ENVIRONMENT_GROUP,
                            "ShowFps",
                            &key_error);

  if (key_error != NULL)
    g_clear_error (&key_error);
  else
    clutter_show_fps = bool_value;

  bool_value =
    g_key_file_get_boolean (keyfile, ENVIRONMENT_GROUP,
                            "DisableMipmappedText",
                            &key_error);

  if (key_error != NULL)
    g_clear_error (&key_error);
  else
    clutter_disable_mipmap_text = bool_value;

  bool_value =
    g_key_file_get_boolean (keyfile, ENVIRONMENT_GROUP,
                            "UseFuzzyPicking",
                            &key_error);

  if (key_error != NULL)
    g_clear_error (&key_error);
  else
    clutter_use_fuzzy_picking = bool_value;

  bool_value =
    g_key_file_get_boolean (keyfile, ENVIRONMENT_GROUP,
                            "EnableAccessibility",
                            &key_error);

  if (key_error != NULL)
    g_clear_error (&key_error);
  else
    clutter_enable_accessibility = bool_value;

  int_value =
    g_key_file_get_integer (keyfile, ENVIRONMENT_GROUP,
                            "DefaultFps",
                            &key_error);

  if (key_error != NULL)
    g_clear_error (&key_error);
  else
    clutter_default_fps = int_value;

  str_value =
    g_key_file_get_string (keyfile, ENVIRONMENT_GROUP,
                           "TextDirection",
                           &key_error);

  if (key_error != NULL)
    g_clear_error (&key_error);
  else
    {
      if (g_strcmp0 (str_value, "rtl") == 0)
        clutter_text_direction = CLUTTER_TEXT_DIRECTION_RTL;
      else
        clutter_text_direction = CLUTTER_TEXT_DIRECTION_LTR;
    }

  g_free (str_value);
}

#ifdef CLUTTER_ENABLE_DEBUG
static void
clutter_debug_read_from_key_file (GKeyFile *keyfile)
{
  GError *key_error = NULL;
  gchar *value;

  if (!g_key_file_has_group (keyfile, DEBUG_GROUP))
    return;

  value = g_key_file_get_value (keyfile, DEBUG_GROUP,
                                "Debug",
                                &key_error);
  if (key_error == NULL)
    {
      clutter_debug_flags |=
        g_parse_debug_string (value,
                              clutter_debug_keys,
                              G_N_ELEMENTS (clutter_debug_keys));
    }
  else
    g_clear_error (&key_error);

  g_free (value);

  value = g_key_file_get_value (keyfile, DEBUG_GROUP,
                                "PaintDebug",
                                &key_error);
  if (key_error == NULL)
    {
      clutter_paint_debug_flags |=
        g_parse_debug_string (value,
                              clutter_paint_debug_keys,
                              G_N_ELEMENTS (clutter_paint_debug_keys));
    }
  else
    g_clear_error (&key_error);

  g_free (value);

  value = g_key_file_get_value (keyfile, DEBUG_GROUP,
                                "PickDebug",
                                &key_error);
  if (key_error == NULL)
    {
      clutter_pick_debug_flags |=
        g_parse_debug_string (value,
                              clutter_pick_debug_keys,
                              G_N_ELEMENTS (clutter_pick_debug_keys));
    }
  else
    g_clear_error (&key_error);

  g_free (value);
}
#endif

static void
clutter_config_read_from_file (const gchar *config_path)
{
  ClutterSettings *settings = clutter_settings_get_default ();
  GKeyFile *key_file = g_key_file_new ();
  GError *error = NULL;

  g_key_file_load_from_file (key_file, config_path, G_KEY_FILE_NONE, &error);
  if (error == NULL)
    {
      clutter_config_read_from_key_file (key_file);
#ifdef CLUTTER_ENABLE_DEBUG
      clutter_debug_read_from_key_file (key_file);
#endif
      _clutter_settings_read_from_key_file (settings, key_file);
    }
  else
    {
      g_warning ("Unable to read configuration settings from '%s': %s",
                 config_path,
                 error->message);
      g_error_free (error);
    }

  g_key_file_free (key_file);
}

static void
clutter_config_read (void)
{
  gchar *config_path;

  config_path = g_build_filename (CLUTTER_SYSCONFDIR,
                                  "clutter-1.0",
                                  "settings.ini",
                                  NULL);
  if (g_file_test (config_path, G_FILE_TEST_EXISTS))
    clutter_config_read_from_file (config_path);

  g_free (config_path);

  config_path = g_build_filename (g_get_user_config_dir (),
                                  "clutter-1.0",
                                  "settings.ini",
                                  NULL);
  if (g_file_test (config_path, G_FILE_TEST_EXISTS))
    clutter_config_read_from_file (config_path);

  g_free (config_path);
}

/**
 * clutter_get_show_fps:
 *
 * Returns whether Clutter should print out the frames per second on the
 * console. You can enable this setting either using the
 * <literal>CLUTTER_SHOW_FPS</literal> environment variable or passing
 * the <literal>--clutter-show-fps</literal> command line argument. *
 *
 * Return value: %TRUE if Clutter should show the FPS.
 *
 * Since: 0.4
 */
gboolean
clutter_get_show_fps (void)
{
  return clutter_show_fps;
}

/**
 * clutter_get_accessibility_enabled:
 *
 * Returns whether Clutter has accessibility support enabled.  As
 * least, a value of TRUE means that there are a proper AtkUtil
 * implementation available
 *
 * Return value: %TRUE if Clutter has accessibility support enabled
 *
 * Since: 1.4
 */
gboolean
clutter_get_accessibility_enabled (void)
{
  return cally_get_cally_initialized ();
}

/**
 * clutter_redraw:
 *
 * Forces a redraw of the entire stage. Applications should never use this
 * function, but queue a redraw using clutter_actor_queue_redraw().
 *
 * This function should only be used by libraries integrating Clutter from
 * within another toolkit.
 *
 * Deprecated: 1.10: Use clutter_stage_ensure_redraw() instead.
 */
void
clutter_redraw (ClutterStage *stage)
{
  g_return_if_fail (CLUTTER_IS_STAGE (stage));

  clutter_stage_ensure_redraw (stage);
}

/**
 * clutter_set_motion_events_enabled:
 * @enable: %TRUE to enable per-actor motion events
 *
 * Sets whether per-actor motion events should be enabled or not on
 * all #ClutterStage<!-- -->s managed by Clutter.
 *
 * If @enable is %FALSE the following events will not work:
 * <itemizedlist>
 *   <listitem><para>ClutterActor::motion-event, unless on the
 *     #ClutterStage</para></listitem>
 *   <listitem><para>ClutterActor::enter-event</para></listitem>
 *   <listitem><para>ClutterActor::leave-event</para></listitem>
 * </itemizedlist>
 *
 * Since: 0.6
 *
 * Deprecated: 1.8: Use clutter_stage_set_motion_events_enabled() instead.
 */
void
clutter_set_motion_events_enabled (gboolean enable)
{
  ClutterStageManager *stage_manager;
  ClutterMainContext *context;
  const GSList *l;

  enable = !!enable;

  context = _clutter_context_get_default ();
  if (context->motion_events_per_actor == enable)
    return;

  /* store the flag for later query and for newly created stages */
  context->motion_events_per_actor = enable;

  /* propagate the change to all stages */
  stage_manager = clutter_stage_manager_get_default ();

  for (l = clutter_stage_manager_peek_stages (stage_manager);
       l != NULL;
       l = l->next)
    {
      clutter_stage_set_motion_events_enabled (l->data, enable);
    }
}

/**
 * clutter_get_motion_events_enabled:
 *
 * Gets whether the per-actor motion events are enabled.
 *
 * Return value: %TRUE if the motion events are enabled
 *
 * Since: 0.6
 *
 * Deprecated: 1.8: Use clutter_stage_get_motion_events_enabled() instead.
 */
gboolean
clutter_get_motion_events_enabled (void)
{
  return _clutter_context_get_motion_events_enabled ();
}

ClutterActor *
_clutter_get_actor_by_id (ClutterStage *stage,
                          guint32       actor_id)
{
  if (stage == NULL)
    {
      ClutterMainContext *context = _clutter_context_get_default ();

      return _clutter_id_pool_lookup (context->id_pool, actor_id);
    }

  return _clutter_stage_get_actor_by_pick_id (stage, actor_id);
}

void
_clutter_id_to_color (guint         id_,
                      ClutterColor *col)
{
  ClutterMainContext *ctx;
  gint red, green, blue;

  ctx = _clutter_context_get_default ();

  if (ctx->fb_g_mask == 0)
    {
      /* Figure out framebuffer masks used for pick */
      cogl_get_bitmasks (&ctx->fb_r_mask,
			 &ctx->fb_g_mask,
			 &ctx->fb_b_mask, NULL);

      ctx->fb_r_mask_used = ctx->fb_r_mask;
      ctx->fb_g_mask_used = ctx->fb_g_mask;
      ctx->fb_b_mask_used = ctx->fb_b_mask;

      /* XXX - describe what "fuzzy picking" is */
      if (clutter_use_fuzzy_picking)
	{
	  ctx->fb_r_mask_used--;
	  ctx->fb_g_mask_used--;
	  ctx->fb_b_mask_used--;
	}
    }

  /* compute the numbers we'll store in the components */
  red   = (id_ >> (ctx->fb_g_mask_used+ctx->fb_b_mask_used))
        & (0xff >> (8-ctx->fb_r_mask_used));
  green = (id_ >> ctx->fb_b_mask_used)
        & (0xff >> (8-ctx->fb_g_mask_used));
  blue  = (id_)
        & (0xff >> (8-ctx->fb_b_mask_used));

  /* shift left bits a bit and add one, this circumvents
   * at least some potential rounding errors in GL/GLES
   * driver / hw implementation.
   */
  if (ctx->fb_r_mask_used != ctx->fb_r_mask)
    red = red * 2;
  if (ctx->fb_g_mask_used != ctx->fb_g_mask)
    green = green * 2;
  if (ctx->fb_b_mask_used != ctx->fb_b_mask)
    blue  = blue  * 2;

  /* shift up to be full 8bit values */
  red   = (red   << (8 - ctx->fb_r_mask)) | (0x7f >> (ctx->fb_r_mask_used));
  green = (green << (8 - ctx->fb_g_mask)) | (0x7f >> (ctx->fb_g_mask_used));
  blue  = (blue  << (8 - ctx->fb_b_mask)) | (0x7f >> (ctx->fb_b_mask_used));

  col->red   = red;
  col->green = green;
  col->blue  = blue;
  col->alpha = 0xff;

  /* XXX: We rotate the nibbles of the colors here so that there is a
   * visible variation between colors of sequential actor identifiers;
   * otherwise pick buffers dumped to an image will pretty much just look
   * black.
   */
  if (G_UNLIKELY (clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS))
    {
      col->red   = (col->red << 4)   | (col->red >> 4);
      col->green = (col->green << 4) | (col->green >> 4);
      col->blue  = (col->blue << 4)  | (col->blue >> 4);
    }
}

guint
_clutter_pixel_to_id (guchar pixel[4])
{
  ClutterMainContext *ctx;
  gint red, green, blue;
  guint retval;

  ctx = _clutter_context_get_default ();

  /* reduce the pixel components to the number of bits actually used of the
   * 8bits.
   */
  if (G_UNLIKELY (clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS))
    {
      guchar tmp;

      /* XXX: In _clutter_id_to_color we rotated the nibbles of the colors so
       * that there is a visible variation between colors of sequential actor
       * identifiers (otherwise pick buffers dumped to an image will pretty
       * much just look black.) Here we reverse that rotation.
       */
      tmp = ((pixel[0] << 4) | (pixel[0] >> 4));
      red = tmp >> (8 - ctx->fb_r_mask);
      tmp = ((pixel[1] << 4) | (pixel[1] >> 4));
      green = tmp >> (8 - ctx->fb_g_mask);
      tmp = ((pixel[2] << 4) | (pixel[2] >> 4));
      blue = tmp >> (8 - ctx->fb_b_mask);
    }
  else
    {
      red   = pixel[0] >> (8 - ctx->fb_r_mask);
      green = pixel[1] >> (8 - ctx->fb_g_mask);
      blue  = pixel[2] >> (8 - ctx->fb_b_mask);
    }

  /* divide potentially by two if 'fuzzy' */
  red   = red   >> (ctx->fb_r_mask - ctx->fb_r_mask_used);
  green = green >> (ctx->fb_g_mask - ctx->fb_g_mask_used);
  blue  = blue  >> (ctx->fb_b_mask - ctx->fb_b_mask_used);

  /* combine the correct per component values into the final id */
  retval = blue
         + (green <<  ctx->fb_b_mask_used)
         + (red << (ctx->fb_b_mask_used + ctx->fb_g_mask_used));

  return retval;
}

static CoglPangoFontMap *
clutter_context_get_pango_fontmap (void)
{
  ClutterMainContext *self;
  CoglPangoFontMap *font_map;
  gdouble resolution;
  gboolean use_mipmapping;

  self = _clutter_context_get_default ();
  if (G_LIKELY (self->font_map != NULL))
    return self->font_map;

  font_map = COGL_PANGO_FONT_MAP (cogl_pango_font_map_new ());

  resolution = clutter_backend_get_resolution (self->backend);
  cogl_pango_font_map_set_resolution (font_map, resolution);

  use_mipmapping = !clutter_disable_mipmap_text;
  cogl_pango_font_map_set_use_mipmapping (font_map, use_mipmapping);

  self->font_map = font_map;

  return self->font_map;
}

static ClutterTextDirection
clutter_get_text_direction (void)
{
  ClutterTextDirection dir = CLUTTER_TEXT_DIRECTION_LTR;
  const gchar *direction;

  direction = g_getenv ("CLUTTER_TEXT_DIRECTION");
  if (direction && *direction != '\0')
    {
      if (strcmp (direction, "rtl") == 0)
        dir = CLUTTER_TEXT_DIRECTION_RTL;
      else if (strcmp (direction, "ltr") == 0)
        dir = CLUTTER_TEXT_DIRECTION_LTR;
    }
  else
    {
      /* Translators: Leave this UNTRANSLATED if your language is
       * left-to-right.  If your language is right-to-left
       * (e.g. Hebrew, Arabic), translate it to "default:RTL".
       *
       * Do NOT translate it to non-English e.g. "predefinito:LTR"! If
       * it isn't default:LTR or default:RTL it will not work.
       */
      char *e = _("default:LTR");

      if (strcmp (e, "default:RTL") == 0)
        dir = CLUTTER_TEXT_DIRECTION_RTL;
      else if (strcmp (e, "default:LTR") == 0)
        dir = CLUTTER_TEXT_DIRECTION_LTR;
      else
        g_warning ("Whoever translated default:LTR did so wrongly.");
    }

  return dir;
}

static void
update_pango_context (ClutterBackend *backend,
                      PangoContext   *context)
{
  ClutterSettings *settings;
  PangoFontDescription *font_desc;
  const cairo_font_options_t *font_options;
  gchar *font_name;
  PangoDirection pango_dir;
  gdouble resolution;

  settings = clutter_settings_get_default ();

  /* update the text direction */
  if (clutter_text_direction == CLUTTER_TEXT_DIRECTION_RTL)
    pango_dir = PANGO_DIRECTION_RTL;
  else
    pango_dir = PANGO_DIRECTION_LTR;

  pango_context_set_base_dir (context, pango_dir);

  g_object_get (settings, "font-name", &font_name, NULL);

  /* get the configuration for the PangoContext from the backend */
  font_options = clutter_backend_get_font_options (backend);
  resolution = clutter_backend_get_resolution (backend);

  font_desc = pango_font_description_from_string (font_name);

  if (resolution < 0)
    resolution = 96.0; /* fall back */

  pango_context_set_font_description (context, font_desc);
  pango_cairo_context_set_font_options (context, font_options);
  pango_cairo_context_set_resolution (context, resolution);

  pango_font_description_free (font_desc);
  g_free (font_name);
}

PangoContext *
_clutter_context_get_pango_context (void)
{
  ClutterMainContext *self = _clutter_context_get_default ();

  if (G_UNLIKELY (self->pango_context == NULL))
    {
      PangoContext *context;

      context = _clutter_context_create_pango_context ();
      self->pango_context = context;

      g_signal_connect (self->backend, "resolution-changed",
                        G_CALLBACK (update_pango_context),
                        self->pango_context);
      g_signal_connect (self->backend, "font-changed",
                        G_CALLBACK (update_pango_context),
                        self->pango_context);
    }
  else
    update_pango_context (self->backend, self->pango_context);

  return self->pango_context;
}

PangoContext *
_clutter_context_create_pango_context (void)
{
  CoglPangoFontMap *font_map;
  PangoContext *context;

  font_map = clutter_context_get_pango_fontmap ();

  context = cogl_pango_font_map_create_context (font_map);
  update_pango_context (clutter_get_default_backend (), context);
  pango_context_set_language (context, pango_language_get_default ());

  return context;
}

/**
 * clutter_main_quit:
 *
 * Terminates the Clutter mainloop.
 */
void
clutter_main_quit (void)
{
  g_return_if_fail (main_loops != NULL);

  g_main_loop_quit (main_loops->data);
}

/**
 * clutter_main_level:
 *
 * Retrieves the depth of the Clutter mainloop.
 *
 * Return value: The level of the mainloop.
 */
gint
clutter_main_level (void)
{
  return clutter_main_loop_level;
}

#ifdef CLUTTER_ENABLE_PROFILE
static gint (*prev_poll) (GPollFD *ufds, guint nfsd, gint timeout_) = NULL;

static gint
timed_poll (GPollFD *ufds,
            guint nfsd,
            gint timeout_)
{
  gint ret;
  CLUTTER_STATIC_TIMER (poll_timer,
                        "Mainloop", /* parent */
                        "Mainloop Idle",
                        "The time spent idle in poll()",
                        0 /* no application private data */);

  CLUTTER_TIMER_START (uprof_get_mainloop_context (), poll_timer);
  ret = prev_poll (ufds, nfsd, timeout_);
  CLUTTER_TIMER_STOP (uprof_get_mainloop_context (), poll_timer);
  return ret;
}
#endif

/**
 * clutter_main:
 *
 * Starts the Clutter mainloop.
 */
void
clutter_main (void)
{
  GMainLoop *loop;
  CLUTTER_STATIC_TIMER (mainloop_timer,
                        NULL, /* no parent */
                        "Mainloop",
                        "The time spent in the clutter mainloop",
                        0 /* no application private data */);

  if (clutter_main_loop_level == 0)
    CLUTTER_TIMER_START (uprof_get_mainloop_context (), mainloop_timer);

  if (!_clutter_context_is_initialized ())
    {
      g_warning ("Called clutter_main() but Clutter wasn't initialised. "
		 "You must call clutter_init() first.");
      return;
    }

  CLUTTER_MARK ();

  clutter_main_loop_level++;

#ifdef CLUTTER_ENABLE_PROFILE
  if (!prev_poll)
    {
      prev_poll = g_main_context_get_poll_func (NULL);
      g_main_context_set_poll_func (NULL, timed_poll);
    }
#endif

  loop = g_main_loop_new (NULL, TRUE);
  main_loops = g_slist_prepend (main_loops, loop);

  if (g_main_loop_is_running (main_loops->data))
    {
      clutter_threads_leave ();
      g_main_loop_run (loop);
      clutter_threads_enter ();
    }

  main_loops = g_slist_remove (main_loops, loop);

  g_main_loop_unref (loop);

  clutter_main_loop_level--;

  CLUTTER_MARK ();

  if (clutter_main_loop_level == 0)
    CLUTTER_TIMER_STOP (uprof_get_mainloop_context (), mainloop_timer);
}

static void
clutter_threads_impl_lock (void)
{
  g_mutex_lock (&clutter_threads_mutex);
}

static void
clutter_threads_impl_unlock (void)
{
  g_mutex_unlock (&clutter_threads_mutex);
}

/**
 * clutter_threads_init:
 *
 * Initialises the Clutter threading mechanism, so that Clutter API can be
 * called by multiple threads, using clutter_threads_enter() and
 * clutter_threads_leave() to mark the critical sections.
 *
 * You must call g_thread_init() before this function.
 *
 * This function must be called before clutter_init().
 *
 * It is safe to call this function multiple times.
 *
 * Since: 0.4
 */
void
clutter_threads_init (void)
{
  if (clutter_threads_lock == NULL)
    clutter_threads_lock = clutter_threads_impl_lock;

  if (clutter_threads_unlock == NULL)
    clutter_threads_unlock = clutter_threads_impl_unlock;
}

/**
 * clutter_threads_set_lock_functions: (skip)
 * @enter_fn: function called when aquiring the Clutter main lock
 * @leave_fn: function called when releasing the Clutter main lock<commit_msg>clutter-main.c: Fix multiple declarations<commit_after>/*
 * Clutter.
 *
 * An OpenGL based 'interactive canvas' library.
 *
 * Authored By <NAME>  <<EMAIL>>
 *
 * Copyright (C) 2006 OpenedHand
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
 *
 *
 */
/**
 * SECTION:clutter-main
 * @short_description: Various 'global' clutter functions.
 *
 * Functions to retrieve various global Clutter resources and other utility
 * functions for mainloops, events and threads
 *
 * <refsect2 id="clutter-Threading-Model">
 *   <title>Threading Model</title>
 *   <para>Clutter is <emphasis>thread-aware</emphasis>: all operations
 *   performed by Clutter are assumed to be under the big Clutter lock,
 *   which is created when the threading is initialized through
 *   clutter_threads_init().</para>
 *   <example id="example-Thread-Init">
 *     <title>Thread Initialization</title>
 *     <para>The code below shows how to correctly initialize Clutter
 *     in a multi-threaded environment. These operations are mandatory for
 *     applications that wish to use threads with Clutter.</para>
 *     <programlisting>
 * int
 * main (int argc, char *argv[])
 * {
 *   /&ast; initialize GLib's threading support &ast;/
 *   g_thread_init (NULL);
 *
 *   /&ast; initialize Clutter's threading support &ast;/
 *   clutter_threads_init ();
 *
 *   /&ast; initialize Clutter &ast;/
 *   clutter_init (&amp;argc, &amp;argv);
 *
 *   /&ast; program code &ast;/
 *
 *   /&ast; acquire the main lock &ast;/
 *   clutter_threads_enter ();
 *
 *   /&ast; start the main loop &ast;/
 *   clutter_main ();
 *
 *   /&ast; release the main lock &ast;/
 *   clutter_threads_leave ();
 *
 *   /&ast; clean up &ast;/
 *   return 0;
 * }
 *     </programlisting>
 *   </example>
 *   <para>This threading model has the caveat that it is only safe to call
 *   Clutter's API when the lock has been acquired &mdash; which happens
 *   between pairs of clutter_threads_enter() and clutter_threads_leave()
 *   calls.</para>
 *   <para>The only safe and portable way to use the Clutter API in a
 *   multi-threaded environment is to never access the API from a thread that
 *   did not call clutter_init() and clutter_main().</para>
 *   <para>The common pattern for using threads with Clutter is to use worker
 *   threads to perform blocking operations and then install idle or timeout
 *   sources with the result when the thread finished.</para>
 *   <para>Clutter provides thread-aware variants of g_idle_add() and
 *   g_timeout_add() that acquire the Clutter lock before invoking the provided
 *   callback: clutter_threads_add_idle() and
 *   clutter_threads_add_timeout().</para>
 *   <para>The example below shows how to use a worker thread to perform a
 *   blocking operation, and perform UI updates using the main loop.</para>
 *   <example id="worker-thread-example">
 *     <title>A worker thread example</title>
 *     <programlisting>
 * <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" parse="text" href="../../../../tests/interactive/test-thread.c">
 *   <xi:fallback>FIXME: MISSING XINCLUDE CONTENT</xi:fallback>
 * </xi:include>
 *     </programlisting>
 *   </example>
 * </refsect2>
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <stdlib.h>
#include <glib/gi18n-lib.h>
#include <locale.h>

#include "clutter-actor.h"
#include "clutter-backend-private.h"
#include "clutter-debug.h"
#include "clutter-device-manager-private.h"
#include "clutter-event.h"
#include "clutter-feature.h"
#include "clutter-frame-source.h"
#include "clutter-main.h"
#include "clutter-master-clock.h"
#include "clutter-private.h"
#include "clutter-profile.h"
#include "clutter-settings-private.h"
#include "clutter-stage-manager.h"
#include "clutter-stage-private.h"
#include "clutter-version.h" 	/* For flavour define */

#include <cogl/cogl.h>
#include <cogl-pango/cogl-pango.h>

#include "cally.h" /* For accessibility support */

/* main context */
static ClutterMainContext *ClutterCntx       = NULL;
G_LOCK_DEFINE_STATIC (ClutterCntx);

/* main lock and locking/unlocking functions */
static GMutex clutter_threads_mutex;
static GCallback clutter_threads_lock        = NULL;
static GCallback clutter_threads_unlock      = NULL;

/* command line options */
static gboolean clutter_is_initialized       = FALSE;
static gboolean clutter_show_fps             = FALSE;
static gboolean clutter_fatal_warnings       = FALSE;
static gboolean clutter_disable_mipmap_text  = FALSE;
static gboolean clutter_use_fuzzy_picking    = FALSE;
static gboolean clutter_enable_accessibility = TRUE;

static guint clutter_default_fps             = 60;

static ClutterTextDirection clutter_text_direction = CLUTTER_TEXT_DIRECTION_LTR;

static guint clutter_main_loop_level         = 0;
static GSList *main_loops                    = NULL;

/* debug flags */
guint clutter_debug_flags       = 0;
guint clutter_paint_debug_flags = 0;
guint clutter_pick_debug_flags  = 0;

/* profile flags */
guint clutter_profile_flags     = 0;

const guint clutter_major_version = CLUTTER_MAJOR_VERSION;
const guint clutter_minor_version = CLUTTER_MINOR_VERSION;
const guint clutter_micro_version = CLUTTER_MICRO_VERSION;

#ifdef CLUTTER_ENABLE_DEBUG
static const GDebugKey clutter_debug_keys[] = {
  { "misc", CLUTTER_DEBUG_MISC },
  { "actor", CLUTTER_DEBUG_ACTOR },
  { "texture", CLUTTER_DEBUG_TEXTURE },
  { "event", CLUTTER_DEBUG_EVENT },
  { "paint", CLUTTER_DEBUG_PAINT },
  { "pick", CLUTTER_DEBUG_PICK },
  { "pango", CLUTTER_DEBUG_PANGO },
  { "backend", CLUTTER_DEBUG_BACKEND },
  { "scheduler", CLUTTER_DEBUG_SCHEDULER },
  { "script", CLUTTER_DEBUG_SCRIPT },
  { "shader", CLUTTER_DEBUG_SHADER },
  { "animation", CLUTTER_DEBUG_ANIMATION },
  { "layout", CLUTTER_DEBUG_LAYOUT },
  { "clipping", CLUTTER_DEBUG_CLIPPING },
  { "oob-transforms", CLUTTER_DEBUG_OOB_TRANSFORMS },
};
#endif /* CLUTTER_ENABLE_DEBUG */

static const GDebugKey clutter_pick_debug_keys[] = {
  { "nop-picking", CLUTTER_DEBUG_NOP_PICKING },
  { "dump-pick-buffers", CLUTTER_DEBUG_DUMP_PICK_BUFFERS },
};

static const GDebugKey clutter_paint_debug_keys[] = {
  { "disable-swap-events", CLUTTER_DEBUG_DISABLE_SWAP_EVENTS },
  { "disable-clipped-redraws", CLUTTER_DEBUG_DISABLE_CLIPPED_REDRAWS },
  { "redraws", CLUTTER_DEBUG_REDRAWS },
  { "paint-volumes", CLUTTER_DEBUG_PAINT_VOLUMES },
  { "disable-culling", CLUTTER_DEBUG_DISABLE_CULLING },
  { "disable-offscreen-redirect", CLUTTER_DEBUG_DISABLE_OFFSCREEN_REDIRECT },
  { "continuous-redraw", CLUTTER_DEBUG_CONTINUOUS_REDRAW },
  { "paint-deform-tiles", CLUTTER_DEBUG_PAINT_DEFORM_TILES },
};

#ifdef CLUTTER_ENABLE_PROFILE
static const GDebugKey clutter_profile_keys[] = {
  {"picking-only", CLUTTER_PROFILE_PICKING_ONLY },
  {"disable-report", CLUTTER_PROFILE_DISABLE_REPORT }
};
#endif /* CLUTTER_ENABLE_DEBUG */

static void
clutter_threads_impl_lock (void)
{
  g_mutex_lock (&clutter_threads_mutex);
}

static void
clutter_threads_impl_unlock (void)
{
  g_mutex_unlock (&clutter_threads_mutex);
}

static inline void
clutter_threads_init_default (void)
{
  g_mutex_init (&clutter_threads_mutex);

  if (clutter_threads_lock == NULL)
    clutter_threads_lock = clutter_threads_impl_lock;

  if (clutter_threads_unlock == NULL)
    clutter_threads_unlock = clutter_threads_impl_unlock;
}

#define ENVIRONMENT_GROUP       "Environment"
#define DEBUG_GROUP             "Debug"

static void
clutter_config_read_from_key_file (GKeyFile *keyfile)
{
  GError *key_error = NULL;
  gboolean bool_value;
  gint int_value;
  gchar *str_value;

  if (!g_key_file_has_group (keyfile, ENVIRONMENT_GROUP))
    return;

  bool_value =
    g_key_file_get_boolean (keyfile, ENVIRONMENT_GROUP,
                            "ShowFps",
                            &key_error);

  if (key_error != NULL)
    g_clear_error (&key_error);
  else
    clutter_show_fps = bool_value;

  bool_value =
    g_key_file_get_boolean (keyfile, ENVIRONMENT_GROUP,
                            "DisableMipmappedText",
                            &key_error);

  if (key_error != NULL)
    g_clear_error (&key_error);
  else
    clutter_disable_mipmap_text = bool_value;

  bool_value =
    g_key_file_get_boolean (keyfile, ENVIRONMENT_GROUP,
                            "UseFuzzyPicking",
                            &key_error);

  if (key_error != NULL)
    g_clear_error (&key_error);
  else
    clutter_use_fuzzy_picking = bool_value;

  bool_value =
    g_key_file_get_boolean (keyfile, ENVIRONMENT_GROUP,
                            "EnableAccessibility",
                            &key_error);

  if (key_error != NULL)
    g_clear_error (&key_error);
  else
    clutter_enable_accessibility = bool_value;

  int_value =
    g_key_file_get_integer (keyfile, ENVIRONMENT_GROUP,
                            "DefaultFps",
                            &key_error);

  if (key_error != NULL)
    g_clear_error (&key_error);
  else
    clutter_default_fps = int_value;

  str_value =
    g_key_file_get_string (keyfile, ENVIRONMENT_GROUP,
                           "TextDirection",
                           &key_error);

  if (key_error != NULL)
    g_clear_error (&key_error);
  else
    {
      if (g_strcmp0 (str_value, "rtl") == 0)
        clutter_text_direction = CLUTTER_TEXT_DIRECTION_RTL;
      else
        clutter_text_direction = CLUTTER_TEXT_DIRECTION_LTR;
    }

  g_free (str_value);
}

#ifdef CLUTTER_ENABLE_DEBUG
static void
clutter_debug_read_from_key_file (GKeyFile *keyfile)
{
  GError *key_error = NULL;
  gchar *value;

  if (!g_key_file_has_group (keyfile, DEBUG_GROUP))
    return;

  value = g_key_file_get_value (keyfile, DEBUG_GROUP,
                                "Debug",
                                &key_error);
  if (key_error == NULL)
    {
      clutter_debug_flags |=
        g_parse_debug_string (value,
                              clutter_debug_keys,
                              G_N_ELEMENTS (clutter_debug_keys));
    }
  else
    g_clear_error (&key_error);

  g_free (value);

  value = g_key_file_get_value (keyfile, DEBUG_GROUP,
                                "PaintDebug",
                                &key_error);
  if (key_error == NULL)
    {
      clutter_paint_debug_flags |=
        g_parse_debug_string (value,
                              clutter_paint_debug_keys,
                              G_N_ELEMENTS (clutter_paint_debug_keys));
    }
  else
    g_clear_error (&key_error);

  g_free (value);

  value = g_key_file_get_value (keyfile, DEBUG_GROUP,
                                "PickDebug",
                                &key_error);
  if (key_error == NULL)
    {
      clutter_pick_debug_flags |=
        g_parse_debug_string (value,
                              clutter_pick_debug_keys,
                              G_N_ELEMENTS (clutter_pick_debug_keys));
    }
  else
    g_clear_error (&key_error);

  g_free (value);
}
#endif

static void
clutter_config_read_from_file (const gchar *config_path)
{
  ClutterSettings *settings = clutter_settings_get_default ();
  GKeyFile *key_file = g_key_file_new ();
  GError *error = NULL;

  g_key_file_load_from_file (key_file, config_path, G_KEY_FILE_NONE, &error);
  if (error == NULL)
    {
      clutter_config_read_from_key_file (key_file);
#ifdef CLUTTER_ENABLE_DEBUG
      clutter_debug_read_from_key_file (key_file);
#endif
      _clutter_settings_read_from_key_file (settings, key_file);
    }
  else
    {
      g_warning ("Unable to read configuration settings from '%s': %s",
                 config_path,
                 error->message);
      g_error_free (error);
    }

  g_key_file_free (key_file);
}

static void
clutter_config_read (void)
{
  gchar *config_path;

  config_path = g_build_filename (CLUTTER_SYSCONFDIR,
                                  "clutter-1.0",
                                  "settings.ini",
                                  NULL);
  if (g_file_test (config_path, G_FILE_TEST_EXISTS))
    clutter_config_read_from_file (config_path);

  g_free (config_path);

  config_path = g_build_filename (g_get_user_config_dir (),
                                  "clutter-1.0",
                                  "settings.ini",
                                  NULL);
  if (g_file_test (config_path, G_FILE_TEST_EXISTS))
    clutter_config_read_from_file (config_path);

  g_free (config_path);
}

/**
 * clutter_get_show_fps:
 *
 * Returns whether Clutter should print out the frames per second on the
 * console. You can enable this setting either using the
 * <literal>CLUTTER_SHOW_FPS</literal> environment variable or passing
 * the <literal>--clutter-show-fps</literal> command line argument. *
 *
 * Return value: %TRUE if Clutter should show the FPS.
 *
 * Since: 0.4
 */
gboolean
clutter_get_show_fps (void)
{
  return clutter_show_fps;
}

/**
 * clutter_get_accessibility_enabled:
 *
 * Returns whether Clutter has accessibility support enabled.  As
 * least, a value of TRUE means that there are a proper AtkUtil
 * implementation available
 *
 * Return value: %TRUE if Clutter has accessibility support enabled
 *
 * Since: 1.4
 */
gboolean
clutter_get_accessibility_enabled (void)
{
  return cally_get_cally_initialized ();
}

/**
 * clutter_redraw:
 *
 * Forces a redraw of the entire stage. Applications should never use this
 * function, but queue a redraw using clutter_actor_queue_redraw().
 *
 * This function should only be used by libraries integrating Clutter from
 * within another toolkit.
 *
 * Deprecated: 1.10: Use clutter_stage_ensure_redraw() instead.
 */
void
clutter_redraw (ClutterStage *stage)
{
  g_return_if_fail (CLUTTER_IS_STAGE (stage));

  clutter_stage_ensure_redraw (stage);
}

/**
 * clutter_set_motion_events_enabled:
 * @enable: %TRUE to enable per-actor motion events
 *
 * Sets whether per-actor motion events should be enabled or not on
 * all #ClutterStage<!-- -->s managed by Clutter.
 *
 * If @enable is %FALSE the following events will not work:
 * <itemizedlist>
 *   <listitem><para>ClutterActor::motion-event, unless on the
 *     #ClutterStage</para></listitem>
 *   <listitem><para>ClutterActor::enter-event</para></listitem>
 *   <listitem><para>ClutterActor::leave-event</para></listitem>
 * </itemizedlist>
 *
 * Since: 0.6
 *
 * Deprecated: 1.8: Use clutter_stage_set_motion_events_enabled() instead.
 */
void
clutter_set_motion_events_enabled (gboolean enable)
{
  ClutterStageManager *stage_manager;
  ClutterMainContext *context;
  const GSList *l;

  enable = !!enable;

  context = _clutter_context_get_default ();
  if (context->motion_events_per_actor == enable)
    return;

  /* store the flag for later query and for newly created stages */
  context->motion_events_per_actor = enable;

  /* propagate the change to all stages */
  stage_manager = clutter_stage_manager_get_default ();

  for (l = clutter_stage_manager_peek_stages (stage_manager);
       l != NULL;
       l = l->next)
    {
      clutter_stage_set_motion_events_enabled (l->data, enable);
    }
}

/**
 * clutter_get_motion_events_enabled:
 *
 * Gets whether the per-actor motion events are enabled.
 *
 * Return value: %TRUE if the motion events are enabled
 *
 * Since: 0.6
 *
 * Deprecated: 1.8: Use clutter_stage_get_motion_events_enabled() instead.
 */
gboolean
clutter_get_motion_events_enabled (void)
{
  return _clutter_context_get_motion_events_enabled ();
}

ClutterActor *
_clutter_get_actor_by_id (ClutterStage *stage,
                          guint32       actor_id)
{
  if (stage == NULL)
    {
      ClutterMainContext *context = _clutter_context_get_default ();

      return _clutter_id_pool_lookup (context->id_pool, actor_id);
    }

  return _clutter_stage_get_actor_by_pick_id (stage, actor_id);
}

void
_clutter_id_to_color (guint         id_,
                      ClutterColor *col)
{
  ClutterMainContext *ctx;
  gint red, green, blue;

  ctx = _clutter_context_get_default ();

  if (ctx->fb_g_mask == 0)
    {
      /* Figure out framebuffer masks used for pick */
      cogl_get_bitmasks (&ctx->fb_r_mask,
			 &ctx->fb_g_mask,
			 &ctx->fb_b_mask, NULL);

      ctx->fb_r_mask_used = ctx->fb_r_mask;
      ctx->fb_g_mask_used = ctx->fb_g_mask;
      ctx->fb_b_mask_used = ctx->fb_b_mask;

      /* XXX - describe what "fuzzy picking" is */
      if (clutter_use_fuzzy_picking)
	{
	  ctx->fb_r_mask_used--;
	  ctx->fb_g_mask_used--;
	  ctx->fb_b_mask_used--;
	}
    }

  /* compute the numbers we'll store in the components */
  red   = (id_ >> (ctx->fb_g_mask_used+ctx->fb_b_mask_used))
        & (0xff >> (8-ctx->fb_r_mask_used));
  green = (id_ >> ctx->fb_b_mask_used)
        & (0xff >> (8-ctx->fb_g_mask_used));
  blue  = (id_)
        & (0xff >> (8-ctx->fb_b_mask_used));

  /* shift left bits a bit and add one, this circumvents
   * at least some potential rounding errors in GL/GLES
   * driver / hw implementation.
   */
  if (ctx->fb_r_mask_used != ctx->fb_r_mask)
    red = red * 2;
  if (ctx->fb_g_mask_used != ctx->fb_g_mask)
    green = green * 2;
  if (ctx->fb_b_mask_used != ctx->fb_b_mask)
    blue  = blue  * 2;

  /* shift up to be full 8bit values */
  red   = (red   << (8 - ctx->fb_r_mask)) | (0x7f >> (ctx->fb_r_mask_used));
  green = (green << (8 - ctx->fb_g_mask)) | (0x7f >> (ctx->fb_g_mask_used));
  blue  = (blue  << (8 - ctx->fb_b_mask)) | (0x7f >> (ctx->fb_b_mask_used));

  col->red   = red;
  col->green = green;
  col->blue  = blue;
  col->alpha = 0xff;

  /* XXX: We rotate the nibbles of the colors here so that there is a
   * visible variation between colors of sequential actor identifiers;
   * otherwise pick buffers dumped to an image will pretty much just look
   * black.
   */
  if (G_UNLIKELY (clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS))
    {
      col->red   = (col->red << 4)   | (col->red >> 4);
      col->green = (col->green << 4) | (col->green >> 4);
      col->blue  = (col->blue << 4)  | (col->blue >> 4);
    }
}

guint
_clutter_pixel_to_id (guchar pixel[4])
{
  ClutterMainContext *ctx;
  gint red, green, blue;
  guint retval;

  ctx = _clutter_context_get_default ();

  /* reduce the pixel components to the number of bits actually used of the
   * 8bits.
   */
  if (G_UNLIKELY (clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS))
    {
      guchar tmp;

      /* XXX: In _clutter_id_to_color we rotated the nibbles of the colors so
       * that there is a visible variation between colors of sequential actor
       * identifiers (otherwise pick buffers dumped to an image will pretty
       * much just look black.) Here we reverse that rotation.
       */
      tmp = ((pixel[0] << 4) | (pixel[0] >> 4));
      red = tmp >> (8 - ctx->fb_r_mask);
      tmp = ((pixel[1] << 4) | (pixel[1] >> 4));
      green = tmp >> (8 - ctx->fb_g_mask);
      tmp = ((pixel[2] << 4) | (pixel[2] >> 4));
      blue = tmp >> (8 - ctx->fb_b_mask);
    }
  else
    {
      red   = pixel[0] >> (8 - ctx->fb_r_mask);
      green = pixel[1] >> (8 - ctx->fb_g_mask);
      blue  = pixel[2] >> (8 - ctx->fb_b_mask);
    }

  /* divide potentially by two if 'fuzzy' */
  red   = red   >> (ctx->fb_r_mask - ctx->fb_r_mask_used);
  green = green >> (ctx->fb_g_mask - ctx->fb_g_mask_used);
  blue  = blue  >> (ctx->fb_b_mask - ctx->fb_b_mask_used);

  /* combine the correct per component values into the final id */
  retval = blue
         + (green <<  ctx->fb_b_mask_used)
         + (red << (ctx->fb_b_mask_used + ctx->fb_g_mask_used));

  return retval;
}

static CoglPangoFontMap *
clutter_context_get_pango_fontmap (void)
{
  ClutterMainContext *self;
  CoglPangoFontMap *font_map;
  gdouble resolution;
  gboolean use_mipmapping;

  self = _clutter_context_get_default ();
  if (G_LIKELY (self->font_map != NULL))
    return self->font_map;

  font_map = COGL_PANGO_FONT_MAP (cogl_pango_font_map_new ());

  resolution = clutter_backend_get_resolution (self->backend);
  cogl_pango_font_map_set_resolution (font_map, resolution);

  use_mipmapping = !clutter_disable_mipmap_text;
  cogl_pango_font_map_set_use_mipmapping (font_map, use_mipmapping);

  self->font_map = font_map;

  return self->font_map;
}

static ClutterTextDirection
clutter_get_text_direction (void)
{
  ClutterTextDirection dir = CLUTTER_TEXT_DIRECTION_LTR;
  const gchar *direction;

  direction = g_getenv ("CLUTTER_TEXT_DIRECTION");
  if (direction && *direction != '\0')
    {
      if (strcmp (direction, "rtl") == 0)
        dir = CLUTTER_TEXT_DIRECTION_RTL;
      else if (strcmp (direction, "ltr") == 0)
        dir = CLUTTER_TEXT_DIRECTION_LTR;
    }
  else
    {
      /* Translators: Leave this UNTRANSLATED if your language is
       * left-to-right.  If your language is right-to-left
       * (e.g. Hebrew, Arabic), translate it to "default:RTL".
       *
       * Do NOT translate it to non-English e.g. "predefinito:LTR"! If
       * it isn't default:LTR or default:RTL it will not work.
       */
      char *e = _("default:LTR");

      if (strcmp (e, "default:RTL") == 0)
        dir = CLUTTER_TEXT_DIRECTION_RTL;
      else if (strcmp (e, "default:LTR") == 0)
        dir = CLUTTER_TEXT_DIRECTION_LTR;
      else
        g_warning ("Whoever translated default:LTR did so wrongly.");
    }

  return dir;
}

static void
update_pango_context (ClutterBackend *backend,
                      PangoContext   *context)
{
  ClutterSettings *settings;
  PangoFontDescription *font_desc;
  const cairo_font_options_t *font_options;
  gchar *font_name;
  PangoDirection pango_dir;
  gdouble resolution;

  settings = clutter_settings_get_default ();

  /* update the text direction */
  if (clutter_text_direction == CLUTTER_TEXT_DIRECTION_RTL)
    pango_dir = PANGO_DIRECTION_RTL;
  else
    pango_dir = PANGO_DIRECTION_LTR;

  pango_context_set_base_dir (context, pango_dir);

  g_object_get (settings, "font-name", &font_name, NULL);

  /* get the configuration for the PangoContext from the backend */
  font_options = clutter_backend_get_font_options (backend);
  resolution = clutter_backend_get_resolution (backend);

  font_desc = pango_font_description_from_string (font_name);

  if (resolution < 0)
    resolution = 96.0; /* fall back */

  pango_context_set_font_description (context, font_desc);
  pango_cairo_context_set_font_options (context, font_options);
  pango_cairo_context_set_resolution (context, resolution);

  pango_font_description_free (font_desc);
  g_free (font_name);
}

PangoContext *
_clutter_context_get_pango_context (void)
{
  ClutterMainContext *self = _clutter_context_get_default ();

  if (G_UNLIKELY (self->pango_context == NULL))
    {
      PangoContext *context;

      context = _clutter_context_create_pango_context ();
      self->pango_context = context;

      g_signal_connect (self->backend, "resolution-changed",
                        G_CALLBACK (update_pango_context),
                        self->pango_context);
      g_signal_connect (self->backend, "font-changed",
                        G_CALLBACK (update_pango_context),
                        self->pango_context);
    }
  else
    update_pango_context (self->backend, self->pango_context);

  return self->pango_context;
}

PangoContext *
_clutter_context_create_pango_context (void)
{
  CoglPangoFontMap *font_map;
  PangoContext *context;

  font_map = clutter_context_get_pango_fontmap ();

  context = cogl_pango_font_map_create_context (font_map);
  update_pango_context (clutter_get_default_backend (), context);
  pango_context_set_language (context, pango_language_get_default ());

  return context;
}

/**
 * clutter_main_quit:
 *
 * Terminates the Clutter mainloop.
 */
void
clutter_main_quit (void)
{
  g_return_if_fail (main_loops != NULL);

  g_main_loop_quit (main_loops->data);
}

/**
 * clutter_main_level:
 *
 * Retrieves the depth of the Clutter mainloop.
 *
 * Return value: The level of the mainloop.
 */
gint
clutter_main_level (void)
{
  return clutter_main_loop_level;
}

#ifdef CLUTTER_ENABLE_PROFILE
static gint (*prev_poll) (GPollFD *ufds, guint nfsd, gint timeout_) = NULL;

static gint
timed_poll (GPollFD *ufds,
            guint nfsd,
            gint timeout_)
{
  gint ret;
  CLUTTER_STATIC_TIMER (poll_timer,
                        "Mainloop", /* parent */
                        "Mainloop Idle",
                        "The time spent idle in poll()",
                        0 /* no application private data */);

  CLUTTER_TIMER_START (uprof_get_mainloop_context (), poll_timer);
  ret = prev_poll (ufds, nfsd, timeout_);
  CLUTTER_TIMER_STOP (uprof_get_mainloop_context (), poll_timer);
  return ret;
}
#endif

/**
 * clutter_main:
 *
 * Starts the Clutter mainloop.
 */
void
clutter_main (void)
{
  GMainLoop *loop;
  CLUTTER_STATIC_TIMER (mainloop_timer,
                        NULL, /* no parent */
                        "Mainloop",
                        "The time spent in the clutter mainloop",
                        0 /* no application private data */);

  if (clutter_main_loop_level == 0)
    CLUTTER_TIMER_START (uprof_get_mainloop_context (), mainloop_timer);

  if (!_clutter_context_is_initialized ())
    {
      g_warning ("Called clutter_main() but Clutter wasn't initialised. "
		 "You must call clutter_init() first.");
      return;
    }

  CLUTTER_MARK ();

  clutter_main_loop_level++;

#ifdef CLUTTER_ENABLE_PROFILE
  if (!prev_poll)
    {
      prev_poll = g_main_context_get_poll_func (NULL);
      g_main_context_set_poll_func (NULL, timed_poll);
    }
#endif

  loop = g_main_loop_new (NULL, TRUE);
  main_loops = g_slist_prepend (main_loops, loop);

  if (g_main_loop_is_running (main_loops->data))
    {
      clutter_threads_leave ();
      g_main_loop_run (loop);
      clutter_threads_enter ();
    }

  main_loops = g_slist_remove (main_loops, loop);

  g_main_loop_unref (loop);

  clutter_main_loop_level--;

  CLUTTER_MARK ();

  if (clutter_main_loop_level == 0)
    CLUTTER_TIMER_STOP (uprof_get_mainloop_context (), mainloop_timer);
}

/**
 * clutter_threads_init:
 *
 * Initialises the Clutter threading mechanism, so that Clutter API can be
 * called by multiple threads, using clutter_threads_enter() and
 * clutter_threads_leave() to mark the critical sections.
 *
 * You must call g_thread_init() before this function.
 *
 * This function must be called before clutter_init().
 *
 * It is safe to call this function multiple times.
 *
 * Since: 0.4
 */
void
clutter_threads_init (void)
{
  if (clutter_threads_lock == NULL)
    clutter_threads_lock = clutter_threads_impl_lock;

  if (clutter_threads_unlock == NULL)
    clutter_threads_unlock = clutter_threads_impl_unlock;
}

/**
 * clutter_threads_set_lock_functions: (skip)
 * @enter_fn: function called when aquiring the Clutter main lock
 * @leave_fn: function called when releasing the Clutter main lock
{'fraction_non_alphanumeric': 0.1032174515740707, 'fraction_numerical': 0.004012694706890891, 'mean_word_length': 3.2213581767785646, 'pattern_counts': {'":': 0, '<': 177, '<?xml version=': 0, '>': 385, 'https://': 0, 'lorem ipsum': 0, 'www.': 4, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 12, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4854866', 'n_tokens_mistral': 18999, 'n_tokens_neox': 18435, 'n_words': 4254}