LCOV - code coverage report
Current view: top level - libgjs-private - gjs-util.c (source / functions) Coverage Total Hit
Test: gjs- Code Coverage Lines: 81.8 % 154 126
Test Date: 2024-03-26 02:45:07 Functions: 75.0 % 20 15
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 53.8 % 52 28

             Branch data     Line data    Source code
       1                 :             : /* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
       2                 :             : /*
       3                 :             :  * SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
       4                 :             :  * SPDX-FileCopyrightText: 2012 Giovanni Campagna <scampa.giovanni@gmail.com>
       5                 :             :  */
       6                 :             : 
       7                 :             : #include <config.h>
       8                 :             : 
       9                 :             : #include <locale.h> /* for setlocale */
      10                 :             : #include <stdbool.h>
      11                 :             : #include <stddef.h> /* for size_t */
      12                 :             : #include <string.h>
      13                 :             : 
      14                 :             : #include <glib-object.h>
      15                 :             : #include <girepository.h>
      16                 :             : #include <glib.h>
      17                 :             : #include <glib/gi18n.h> /* for bindtextdomain, bind_textdomain_codeset, textdomain */
      18                 :             : 
      19                 :             : #include "libgjs-private/gjs-util.h"
      20                 :             : #include "util/console.h"
      21                 :             : 
      22                 :             : char *
      23                 :           0 : gjs_format_int_alternative_output(int n)
      24                 :             : {
      25                 :             : #ifdef HAVE_PRINTF_ALTERNATIVE_INT
      26                 :           0 :     return g_strdup_printf("%Id", n);
      27                 :             : #else
      28                 :             :     return g_strdup_printf("%d", n);
      29                 :             : #endif
      30                 :             : }
      31                 :             : 
      32                 :           5 : GType gjs_locale_category_get_type(void) {
      33                 :             :     static size_t gjs_locale_category_get_type = 0;
      34   [ +  -  +  -  :           5 :     if (g_once_init_enter(&gjs_locale_category_get_type)) {
                   +  - ]
      35                 :             :         static const GEnumValue v[] = {
      36                 :             :             {GJS_LOCALE_CATEGORY_ALL, "GJS_LOCALE_CATEGORY_ALL", "all"},
      37                 :             :             {GJS_LOCALE_CATEGORY_COLLATE, "GJS_LOCALE_CATEGORY_COLLATE",
      38                 :             :              "collate"},
      39                 :             :             {GJS_LOCALE_CATEGORY_CTYPE, "GJS_LOCALE_CATEGORY_CTYPE", "ctype"},
      40                 :             :             {GJS_LOCALE_CATEGORY_MESSAGES, "GJS_LOCALE_CATEGORY_MESSAGES",
      41                 :             :              "messages"},
      42                 :             :             {GJS_LOCALE_CATEGORY_MONETARY, "GJS_LOCALE_CATEGORY_MONETARY",
      43                 :             :              "monetary"},
      44                 :             :             {GJS_LOCALE_CATEGORY_NUMERIC, "GJS_LOCALE_CATEGORY_NUMERIC",
      45                 :             :              "numeric"},
      46                 :             :             {GJS_LOCALE_CATEGORY_TIME, "GJS_LOCALE_CATEGORY_TIME", "time"},
      47                 :             :             {0, NULL, NULL}};
      48                 :           5 :         GType g_define_type_id = g_enum_register_static(
      49                 :             :             g_intern_static_string("GjsLocaleCategory"), v);
      50                 :             : 
      51                 :           5 :         g_once_init_leave(&gjs_locale_category_get_type, g_define_type_id);
      52                 :             :     }
      53                 :           5 :     return gjs_locale_category_get_type;
      54                 :             : }
      55                 :             : 
      56                 :             : /**
      57                 :             :  * gjs_setlocale:
      58                 :             :  * @category:
      59                 :             :  * @locale: (allow-none):
      60                 :             :  *
      61                 :             :  * Returns:
      62                 :             :  */
      63                 :             : const char *
      64                 :           4 : gjs_setlocale(GjsLocaleCategory category, const char *locale)
      65                 :             : {
      66                 :             :     /* According to man setlocale(3), the return value may be allocated in
      67                 :             :      * static storage. */
      68                 :           4 :     return (const char *) setlocale(category, locale);
      69                 :             : }
      70                 :             : 
      71                 :             : void
      72                 :           0 : gjs_textdomain(const char *domain)
      73                 :             : {
      74                 :           0 :     textdomain(domain);
      75                 :           0 : }
      76                 :             : 
      77                 :             : void
      78                 :           0 : gjs_bindtextdomain(const char *domain,
      79                 :             :                    const char *location)
      80                 :             : {
      81                 :           0 :     bindtextdomain(domain, location);
      82                 :             :     /* Always use UTF-8; we assume it internally here */
      83                 :           0 :     bind_textdomain_codeset(domain, "UTF-8");
      84                 :           0 : }
      85                 :             : 
      86                 :             : GParamFlags
      87                 :         110 : gjs_param_spec_get_flags(GParamSpec *pspec)
      88                 :             : {
      89                 :         110 :     return pspec->flags;
      90                 :             : }
      91                 :             : 
      92                 :             : GType
      93                 :           2 : gjs_param_spec_get_value_type(GParamSpec *pspec)
      94                 :             : {
      95                 :           2 :     return pspec->value_type;
      96                 :             : }
      97                 :             : 
      98                 :             : GType
      99                 :           0 : gjs_param_spec_get_owner_type(GParamSpec *pspec)
     100                 :             : {
     101                 :           0 :     return pspec->owner_type;
     102                 :             : }
     103                 :             : 
     104                 :             : #define G_CLOSURE_NOTIFY(func) ((GClosureNotify)(void (*)(void))func)
     105                 :             : 
     106                 :           1 : GBinding* gjs_g_object_bind_property_full(
     107                 :             :     GObject* source, const char* source_property, GObject* target,
     108                 :             :     const char* target_property, GBindingFlags flags,
     109                 :             :     GjsBindingTransformFunc to_callback, void* to_data,
     110                 :             :     GDestroyNotify to_notify, GjsBindingTransformFunc from_callback,
     111                 :             :     void* from_data, GDestroyNotify from_notify) {
     112                 :           1 :     GClosure* to_closure = NULL;
     113                 :           1 :     GClosure* from_closure = NULL;
     114                 :             : 
     115         [ +  - ]:           1 :     if (to_callback)
     116                 :           1 :         to_closure = g_cclosure_new(G_CALLBACK(to_callback), to_data,
     117                 :             :                                     G_CLOSURE_NOTIFY(to_notify));
     118                 :             : 
     119         [ -  + ]:           1 :     if (from_callback)
     120                 :           0 :         from_closure = g_cclosure_new(G_CALLBACK(from_callback), from_data,
     121                 :             :                                       G_CLOSURE_NOTIFY(from_notify));
     122                 :             : 
     123                 :           1 :     return g_object_bind_property_with_closures(source, source_property, target,
     124                 :             :                                                 target_property, flags,
     125                 :             :                                                 to_closure, from_closure);
     126                 :             : }
     127                 :             : 
     128                 :             : #if GLIB_CHECK_VERSION(2, 72, 0)
     129                 :           1 : void gjs_g_binding_group_bind_full(
     130                 :             :     GBindingGroup* source, const char* source_property, GObject* target,
     131                 :             :     const char* target_property, GBindingFlags flags,
     132                 :             :     GjsBindingTransformFunc to_callback, void* to_data,
     133                 :             :     GDestroyNotify to_notify, GjsBindingTransformFunc from_callback,
     134                 :             :     void* from_data, GDestroyNotify from_notify) {
     135                 :           1 :     GClosure* to_closure = NULL;
     136                 :           1 :     GClosure* from_closure = NULL;
     137                 :             : 
     138         [ +  - ]:           1 :     if (to_callback)
     139                 :           1 :         to_closure = g_cclosure_new(G_CALLBACK(to_callback), to_data,
     140                 :             :                                     G_CLOSURE_NOTIFY(to_notify));
     141                 :             : 
     142         [ -  + ]:           1 :     if (from_callback)
     143                 :           0 :         from_closure = g_cclosure_new(G_CALLBACK(from_callback), from_data,
     144                 :             :                                       G_CLOSURE_NOTIFY(from_notify));
     145                 :             : 
     146                 :           1 :     g_binding_group_bind_with_closures(source, source_property, target,
     147                 :             :                                        target_property, flags,
     148                 :             :                                        to_closure, from_closure);
     149                 :           1 : }
     150                 :             : #endif
     151                 :             : 
     152                 :             : #undef G_CLOSURE_NOTIFY
     153                 :             : 
     154                 :           1 : static GParamSpec* gjs_gtk_container_class_find_child_property(
     155                 :             :     GIObjectInfo* container_info, GObject* container, const char* property) {
     156                 :           1 :     GIBaseInfo* class_info = NULL;
     157                 :           1 :     GIBaseInfo* find_child_property_fun = NULL;
     158                 :             : 
     159                 :             :     GIArgument ret;
     160                 :             :     GIArgument find_child_property_args[2];
     161                 :             : 
     162                 :           1 :     class_info = g_object_info_get_class_struct(container_info);
     163                 :           1 :     find_child_property_fun =
     164                 :           1 :         g_struct_info_find_method(class_info, "find_child_property");
     165                 :             : 
     166                 :           1 :     find_child_property_args[0].v_pointer = G_OBJECT_GET_CLASS(container);
     167                 :           1 :     find_child_property_args[1].v_string = (char*)property;
     168                 :             : 
     169                 :           1 :     g_function_info_invoke(find_child_property_fun, find_child_property_args, 2,
     170                 :             :                            NULL, 0, &ret, NULL);
     171                 :             : 
     172         [ +  - ]:           1 :     g_clear_pointer(&class_info, g_base_info_unref);
     173         [ +  - ]:           1 :     g_clear_pointer(&find_child_property_fun, g_base_info_unref);
     174                 :             : 
     175                 :           1 :     return (GParamSpec*)ret.v_pointer;
     176                 :             : }
     177                 :             : 
     178                 :           1 : void gjs_gtk_container_child_set_property(GObject* container, GObject* child,
     179                 :             :                                           const char* property,
     180                 :             :                                           const GValue* value) {
     181                 :           1 :     GParamSpec* pspec = NULL;
     182                 :           1 :     GIBaseInfo* base_info = NULL;
     183                 :           1 :     GIBaseInfo* child_set_property_fun = NULL;
     184                 :             :     GIObjectInfo* container_info;
     185                 :           1 :     GValue value_arg = G_VALUE_INIT;
     186                 :             :     GIArgument ret;
     187                 :             : 
     188                 :             :     GIArgument child_set_property_args[4];
     189                 :             : 
     190                 :           1 :     base_info = g_irepository_find_by_name(NULL, "Gtk", "Container");
     191                 :           1 :     container_info = (GIObjectInfo*)base_info;
     192                 :             : 
     193                 :           1 :     pspec = gjs_gtk_container_class_find_child_property(container_info,
     194                 :             :                                                         container, property);
     195         [ -  + ]:           1 :     if (pspec == NULL) {
     196                 :           0 :         g_warning("%s does not have a property called %s",
     197                 :             :                   g_type_name(G_OBJECT_TYPE(container)), property);
     198                 :           0 :         goto out;
     199                 :             :     }
     200                 :             : 
     201   [ +  -  +  - ]:           2 :     if ((G_VALUE_TYPE(value) == G_TYPE_POINTER) &&
     202         [ +  - ]:           2 :         (g_value_get_pointer(value) == NULL) &&
     203                 :           1 :         !g_value_type_transformable(G_VALUE_TYPE(value), pspec->value_type)) {
     204                 :             :         /* Set an empty value. This will happen when we set a NULL value from
     205                 :             :          * JS. Since GJS doesn't know the GParamSpec for this property, it will
     206                 :             :          * just put NULL into a G_TYPE_POINTER GValue, which will later fail
     207                 :             :          * when trying to transform it to the GParamSpec's GType.
     208                 :             :          */
     209                 :           1 :         g_value_init(&value_arg, pspec->value_type);
     210                 :             :     } else {
     211                 :           0 :         g_value_init(&value_arg, G_VALUE_TYPE(value));
     212                 :           0 :         g_value_copy(value, &value_arg);
     213                 :             :     }
     214                 :             : 
     215                 :           1 :     child_set_property_fun =
     216                 :           1 :         g_object_info_find_method(container_info, "child_set_property");
     217                 :             : 
     218                 :           1 :     child_set_property_args[0].v_pointer = container;
     219                 :           1 :     child_set_property_args[1].v_pointer = child;
     220                 :           1 :     child_set_property_args[2].v_string = (char*)property;
     221                 :           1 :     child_set_property_args[3].v_pointer = &value_arg;
     222                 :             : 
     223                 :           1 :     g_function_info_invoke(child_set_property_fun, child_set_property_args, 4,
     224                 :             :                            NULL, 0, &ret, NULL);
     225                 :             : 
     226                 :           1 :     g_value_unset(&value_arg);
     227                 :             : 
     228                 :           1 : out:
     229         [ +  - ]:           1 :     g_clear_pointer(&base_info, g_base_info_unref);
     230         [ +  - ]:           1 :     g_clear_pointer(&child_set_property_fun, g_base_info_unref);
     231                 :           1 : }
     232                 :             : 
     233                 :             : /**
     234                 :             :  * gjs_list_store_insert_sorted:
     235                 :             :  * @store: a #GListStore
     236                 :             :  * @item: the new item
     237                 :             :  * @compare_func: (scope call): pairwise comparison function for sorting
     238                 :             :  * @user_data: (closure): user data for @compare_func
     239                 :             :  *
     240                 :             :  * Inserts @item into @store at a position to be determined by the
     241                 :             :  * @compare_func.
     242                 :             :  *
     243                 :             :  * The list must already be sorted before calling this function or the
     244                 :             :  * result is undefined.  Usually you would approach this by only ever
     245                 :             :  * inserting items by way of this function.
     246                 :             :  *
     247                 :             :  * This function takes a ref on @item.
     248                 :             :  *
     249                 :             :  * Returns: the position at which @item was inserted
     250                 :             :  */
     251                 :          10 : unsigned int gjs_list_store_insert_sorted(GListStore *store, GObject *item,
     252                 :             :                                           GjsCompareDataFunc compare_func,
     253                 :             :                                           void *user_data) {
     254                 :          10 :   return g_list_store_insert_sorted(store, item, (GCompareDataFunc)compare_func, user_data);
     255                 :             : }
     256                 :             : 
     257                 :             : /**
     258                 :             :  * gjs_list_store_sort:
     259                 :             :  * @store: a #GListStore
     260                 :             :  * @compare_func: (scope call): pairwise comparison function for sorting
     261                 :             :  * @user_data: (closure): user data for @compare_func
     262                 :             :  *
     263                 :             :  * Sort the items in @store according to @compare_func.
     264                 :             :  */
     265                 :           1 : void gjs_list_store_sort(GListStore *store, GjsCompareDataFunc compare_func,
     266                 :             :                          void *user_data) {
     267                 :           1 :   g_list_store_sort(store, (GCompareDataFunc)compare_func, user_data);
     268                 :           1 : }
     269                 :             : 
     270                 :             : /**
     271                 :             :  * gjs_gtk_custom_sorter_new:
     272                 :             :  * @sort_func: (nullable) (scope call): function to sort items
     273                 :             :  * @user_data: (closure): user data for @compare_func
     274                 :             :  * @destroy: destroy notify for @user_data
     275                 :             :  *
     276                 :             :  * Creates a new `GtkSorter` that works by calling @sort_func to compare items.
     277                 :             :  *
     278                 :             :  * If @sort_func is %NULL, all items are considered equal.
     279                 :             :  *
     280                 :             :  * Returns: (transfer full): a new `GtkCustomSorter`
     281                 :             :  */
     282                 :           2 : GObject* gjs_gtk_custom_sorter_new(GjsCompareDataFunc sort_func,
     283                 :             :                                    void* user_data, GDestroyNotify destroy) {
     284                 :           2 :     GIObjectInfo* container_info =
     285                 :           2 :         g_irepository_find_by_name(NULL, "Gtk", "CustomSorter");
     286                 :           2 :     GIBaseInfo* custom_sorter_new_fun =
     287                 :           2 :         g_object_info_find_method(container_info, "new");
     288                 :             : 
     289                 :             :     GIArgument ret;
     290                 :             :     GIArgument custom_sorter_new_args[3];
     291                 :           2 :     custom_sorter_new_args[0].v_pointer = sort_func;
     292                 :           2 :     custom_sorter_new_args[1].v_pointer = user_data;
     293                 :           2 :     custom_sorter_new_args[2].v_pointer = destroy;
     294                 :             : 
     295                 :           2 :     g_function_info_invoke(custom_sorter_new_fun, custom_sorter_new_args, 3,
     296                 :             :                            NULL, 0, &ret, NULL);
     297                 :             : 
     298         [ +  - ]:           2 :     g_clear_pointer(&container_info, g_base_info_unref);
     299         [ +  - ]:           2 :     g_clear_pointer(&custom_sorter_new_fun, g_base_info_unref);
     300                 :             : 
     301                 :           2 :     return (GObject*)ret.v_pointer;
     302                 :             : }
     303                 :             : 
     304                 :             : /**
     305                 :             :  * gjs_gtk_custom_sorter_set_sort_func:
     306                 :             :  * @sorter: a `GtkCustomSorter`
     307                 :             :  * @sort_func: (nullable) (scope call): function to sort items
     308                 :             :  * @user_data: (closure): user data to pass to @sort_func
     309                 :             :  * @destroy: destroy notify for @user_data
     310                 :             :  *
     311                 :             :  * Sets (or unsets) the function used for sorting items.
     312                 :             :  *
     313                 :             :  * If @sort_func is %NULL, all items are considered equal.
     314                 :             :  *
     315                 :             :  * If the sort func changes its sorting behavior, gtk_sorter_changed() needs to
     316                 :             :  * be called.
     317                 :             :  *
     318                 :             :  * If a previous function was set, its @user_destroy will be called now.
     319                 :             :  */
     320                 :           2 : void gjs_gtk_custom_sorter_set_sort_func(GObject* sorter,
     321                 :             :                                          GjsCompareDataFunc sort_func,
     322                 :             :                                          void* user_data,
     323                 :             :                                          GDestroyNotify destroy) {
     324                 :           2 :     GIObjectInfo* container_info =
     325                 :           2 :         g_irepository_find_by_name(NULL, "Gtk", "CustomSorter");
     326                 :           2 :     GIBaseInfo* set_sort_func_fun =
     327                 :           2 :         g_object_info_find_method(container_info, "set_sort_func");
     328                 :             : 
     329                 :             :     GIArgument unused_ret;
     330                 :             :     GIArgument set_sort_func_args[4];
     331                 :           2 :     set_sort_func_args[0].v_pointer = sorter;
     332                 :           2 :     set_sort_func_args[1].v_pointer = sort_func;
     333                 :           2 :     set_sort_func_args[2].v_pointer = user_data;
     334                 :           2 :     set_sort_func_args[3].v_pointer = destroy;
     335                 :             : 
     336                 :           2 :     g_function_info_invoke(set_sort_func_fun, set_sort_func_args, 4, NULL, 0,
     337                 :             :                            &unused_ret, NULL);
     338                 :             : 
     339         [ +  - ]:           2 :     g_clear_pointer(&container_info, g_base_info_unref);
     340         [ +  - ]:           2 :     g_clear_pointer(&set_sort_func_fun, g_base_info_unref);
     341                 :           2 : }
     342                 :             : 
     343                 :             : static void* log_writer_user_data = NULL;
     344                 :             : static GDestroyNotify log_writer_user_data_free = NULL;
     345                 :             : static GThread* log_writer_thread = NULL;
     346                 :             : 
     347                 :          83 : static GLogWriterOutput gjs_log_writer_func_wrapper(GLogLevelFlags log_level,
     348                 :             :                                                     const GLogField* fields,
     349                 :             :                                                     size_t n_fields,
     350                 :             :                                                     void* user_data) {
     351                 :          83 :     g_assert(log_writer_thread);
     352                 :             : 
     353                 :             :     // If the wrapper is called from a thread other than the one that set it,
     354                 :             :     // return unhandled so the fallback logger is used.
     355         [ -  + ]:          83 :     if (g_thread_self() != log_writer_thread)
     356                 :           0 :         return g_log_writer_default(log_level, fields, n_fields, NULL);
     357                 :             : 
     358                 :          83 :     GjsGLogWriterFunc func = (GjsGLogWriterFunc)user_data;
     359                 :             :     GVariantDict dict;
     360                 :          83 :     g_variant_dict_init(&dict, NULL);
     361                 :             : 
     362                 :             :     size_t f;
     363         [ +  + ]:         418 :     for (f = 0; f < n_fields; f++) {
     364                 :         335 :         const GLogField* field = &fields[f];
     365                 :             : 
     366                 :             :         GVariant* value;
     367         [ +  + ]:         335 :         if (field->length < 0) {
     368                 :         334 :             size_t bytes_len = strlen(field->value);
     369                 :         334 :             GBytes* bytes = g_bytes_new(field->value, bytes_len);
     370                 :             : 
     371                 :         334 :             value = g_variant_new_maybe(
     372                 :             :                 G_VARIANT_TYPE_BYTESTRING,
     373                 :             :                 g_variant_new_from_bytes(G_VARIANT_TYPE_BYTESTRING, bytes,
     374                 :             :                                          true));
     375                 :         334 :             g_bytes_unref(bytes);
     376         [ +  - ]:           1 :         } else if (field->length > 0) {
     377                 :           1 :             GBytes* bytes = g_bytes_new(field->value, field->length);
     378                 :             : 
     379                 :           1 :             value = g_variant_new_maybe(
     380                 :             :                 G_VARIANT_TYPE_BYTESTRING,
     381                 :             :                 g_variant_new_from_bytes(G_VARIANT_TYPE_BYTESTRING, bytes,
     382                 :             :                                          true));
     383                 :           1 :             g_bytes_unref(bytes);
     384                 :             :         } else {
     385                 :           0 :             value = g_variant_new_maybe(G_VARIANT_TYPE_STRING, NULL);
     386                 :             :         }
     387                 :             : 
     388                 :         335 :         g_variant_dict_insert_value(&dict, field->key, value);
     389                 :             :     }
     390                 :             : 
     391                 :          83 :     GVariant* string_fields = g_variant_dict_end(&dict);
     392                 :          83 :     g_variant_ref(string_fields);
     393                 :             : 
     394                 :             :     GLogWriterOutput output =
     395                 :          83 :         func(log_level, string_fields, log_writer_user_data);
     396                 :             : 
     397                 :          83 :     g_variant_unref(string_fields);
     398                 :             : 
     399                 :             :     // If the function did not handle the log, fallback to the default
     400                 :             :     // handler.
     401         [ +  + ]:          83 :     if (output == G_LOG_WRITER_UNHANDLED)
     402                 :           6 :         return g_log_writer_default(log_level, fields, n_fields, NULL);
     403                 :             : 
     404                 :          77 :     return output;
     405                 :             : }
     406                 :             : 
     407                 :             : /**
     408                 :             :  * gjs_log_set_writer_default:
     409                 :             :  *
     410                 :             :  * Sets the structured logging writer function back to the platform default.
     411                 :             :  */
     412                 :           0 : void gjs_log_set_writer_default() {
     413         [ #  # ]:           0 :     if (log_writer_user_data_free) {
     414                 :           0 :         log_writer_user_data_free(log_writer_user_data);
     415                 :             :     }
     416                 :             : 
     417                 :           0 :     g_log_set_writer_func(g_log_writer_default, NULL, NULL);
     418                 :           0 :     log_writer_user_data_free = NULL;
     419                 :           0 :     log_writer_user_data = NULL;
     420                 :           0 :     log_writer_thread = NULL;
     421                 :           0 : }
     422                 :             : 
     423                 :             : /**
     424                 :             :  * gjs_log_set_writer_func:
     425                 :             :  * @func: (scope notified): callback with log data
     426                 :             :  * @user_data: (closure): user data for @func
     427                 :             :  * @user_data_free: (destroy user_data_free): destroy for @user_data
     428                 :             :  *
     429                 :             :  * Sets a given function as the writer function for structured logging,
     430                 :             :  * passing log fields as a variant. If called from JavaScript the application
     431                 :             :  * must call gjs_log_set_writer_default prior to exiting.
     432                 :             :  */
     433                 :           4 : void gjs_log_set_writer_func(GjsGLogWriterFunc func, void* user_data,
     434                 :             :                              GDestroyNotify user_data_free) {
     435                 :           4 :     log_writer_user_data = user_data;
     436                 :           4 :     log_writer_user_data_free = user_data_free;
     437                 :           4 :     log_writer_thread = g_thread_self();
     438                 :             : 
     439                 :           4 :     g_log_set_writer_func(gjs_log_writer_func_wrapper, func, NULL);
     440                 :           4 : }
     441                 :             : 
     442                 :             : /**
     443                 :             :  * gjs_clear_terminal:
     444                 :             :  *
     445                 :             :  * Clears the terminal, if possible.
     446                 :             :  */
     447                 :           2 : void gjs_clear_terminal(void) {
     448         [ +  - ]:           2 :     if (!gjs_console_is_tty(stdout_fd))
     449                 :           2 :         return;
     450                 :             : 
     451                 :           0 :     gjs_console_clear();
     452                 :             : }
        

Generated by: LCOV version 2.0-1