mirror of
https://github.com/brl/mutter.git
synced 2024-12-04 22:00:42 -05:00
ff4d87727b
Dropped obsolete Free Software Foundation address pointing to the FSF website instead as suggested by https://www.gnu.org/licenses/gpl-howto.html keeping intact the important part of the historical notice as requested by the license. Resolving rpmlint reported issue E: incorrect-fsf-address. Signed-off-by: Sandro Bonazzola <sbonazzo@redhat.com> Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3155>
641 lines
18 KiB
C
641 lines
18 KiB
C
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
|
|
|
/*
|
|
* Copyright (C) 2017 Red Hat
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 2 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* This program 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
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "backends/meta-settings-private.h"
|
|
|
|
#include <gio/gio.h>
|
|
|
|
#include "backends/meta-backend-private.h"
|
|
#include "backends/meta-logical-monitor.h"
|
|
#include "backends/meta-monitor-manager-private.h"
|
|
|
|
#ifndef XWAYLAND_GRAB_DEFAULT_ACCESS_RULES
|
|
# warning "XWAYLAND_GRAB_DEFAULT_ACCESS_RULES is not set"
|
|
# define XWAYLAND_GRAB_DEFAULT_ACCESS_RULES ""
|
|
#endif
|
|
|
|
enum
|
|
{
|
|
UI_SCALING_FACTOR_CHANGED,
|
|
GLOBAL_SCALING_FACTOR_CHANGED,
|
|
FONT_DPI_CHANGED,
|
|
EXPERIMENTAL_FEATURES_CHANGED,
|
|
PRIVACY_SCREEN_CHANGED,
|
|
|
|
N_SIGNALS
|
|
};
|
|
|
|
static guint signals[N_SIGNALS];
|
|
|
|
struct _MetaSettings
|
|
{
|
|
GObject parent;
|
|
|
|
MetaBackend *backend;
|
|
|
|
GSettings *interface_settings;
|
|
GSettings *mutter_settings;
|
|
GSettings *privacy_settings;
|
|
GSettings *wayland_settings;
|
|
|
|
int ui_scaling_factor;
|
|
int global_scaling_factor;
|
|
|
|
int font_dpi;
|
|
|
|
gboolean privacy_screen;
|
|
|
|
MetaExperimentalFeature experimental_features;
|
|
gboolean experimental_features_overridden;
|
|
|
|
gboolean xwayland_allow_grabs;
|
|
GPtrArray *xwayland_grab_allow_list_patterns;
|
|
GPtrArray *xwayland_grab_deny_list_patterns;
|
|
|
|
/* A bitmask of MetaXwaylandExtension enum */
|
|
int xwayland_disable_extensions;
|
|
|
|
/* Whether Xwayland should allow X11 clients from different endianness */
|
|
gboolean xwayland_allow_byte_swapped_clients;
|
|
};
|
|
|
|
G_DEFINE_TYPE (MetaSettings, meta_settings, G_TYPE_OBJECT)
|
|
|
|
static int
|
|
calculate_ui_scaling_factor (MetaSettings *settings)
|
|
{
|
|
MetaMonitorManager *monitor_manager =
|
|
meta_backend_get_monitor_manager (settings->backend);
|
|
MetaLogicalMonitor *primary_logical_monitor;
|
|
|
|
primary_logical_monitor =
|
|
meta_monitor_manager_get_primary_logical_monitor (monitor_manager);
|
|
if (!primary_logical_monitor)
|
|
return 1;
|
|
|
|
return (int) meta_logical_monitor_get_scale (primary_logical_monitor);
|
|
}
|
|
|
|
static gboolean
|
|
update_ui_scaling_factor (MetaSettings *settings)
|
|
{
|
|
int ui_scaling_factor;
|
|
|
|
if (meta_backend_is_stage_views_scaled (settings->backend))
|
|
ui_scaling_factor = 1;
|
|
else
|
|
ui_scaling_factor = calculate_ui_scaling_factor (settings);
|
|
|
|
if (settings->ui_scaling_factor != ui_scaling_factor)
|
|
{
|
|
settings->ui_scaling_factor = ui_scaling_factor;
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
void
|
|
meta_settings_update_ui_scaling_factor (MetaSettings *settings)
|
|
{
|
|
if (update_ui_scaling_factor (settings))
|
|
g_signal_emit (settings, signals[UI_SCALING_FACTOR_CHANGED], 0);
|
|
}
|
|
|
|
int
|
|
meta_settings_get_ui_scaling_factor (MetaSettings *settings)
|
|
{
|
|
g_assert (settings->ui_scaling_factor != 0);
|
|
|
|
return settings->ui_scaling_factor;
|
|
}
|
|
|
|
static gboolean
|
|
update_global_scaling_factor (MetaSettings *settings)
|
|
{
|
|
int global_scaling_factor;
|
|
|
|
global_scaling_factor =
|
|
(int) g_settings_get_uint (settings->interface_settings,
|
|
"scaling-factor");
|
|
|
|
if (settings->global_scaling_factor != global_scaling_factor)
|
|
{
|
|
settings->global_scaling_factor = global_scaling_factor;
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
gboolean
|
|
meta_settings_get_global_scaling_factor (MetaSettings *settings,
|
|
int *out_scaling_factor)
|
|
{
|
|
if (settings->global_scaling_factor == 0)
|
|
return FALSE;
|
|
|
|
*out_scaling_factor = settings->global_scaling_factor;
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
update_font_dpi (MetaSettings *settings)
|
|
{
|
|
double text_scaling_factor;
|
|
/* Number of logical pixels on an inch when unscaled */
|
|
const double dots_per_inch = 96;
|
|
/* Being based on Xft, API users expect the DPI to be 1/1024th of an inch. */
|
|
const double xft_factor = 1024;
|
|
int font_dpi;
|
|
|
|
text_scaling_factor = g_settings_get_double (settings->interface_settings,
|
|
"text-scaling-factor");
|
|
font_dpi = (int) (text_scaling_factor *
|
|
dots_per_inch *
|
|
xft_factor *
|
|
settings->ui_scaling_factor);
|
|
|
|
if (font_dpi != settings->font_dpi)
|
|
{
|
|
settings->font_dpi = font_dpi;
|
|
|
|
g_object_set (clutter_settings_get_default (),
|
|
"font-dpi", font_dpi,
|
|
NULL);
|
|
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
static void
|
|
meta_settings_update_font_dpi (MetaSettings *settings)
|
|
{
|
|
if (update_font_dpi (settings))
|
|
g_signal_emit (settings, signals[FONT_DPI_CHANGED], 0);
|
|
}
|
|
|
|
int
|
|
meta_settings_get_font_dpi (MetaSettings *settings)
|
|
{
|
|
g_assert (settings->font_dpi != 0);
|
|
|
|
return settings->font_dpi;
|
|
}
|
|
|
|
static void
|
|
interface_settings_changed (GSettings *interface_settings,
|
|
const char *key,
|
|
MetaSettings *settings)
|
|
{
|
|
if (g_str_equal (key, "scaling-factor"))
|
|
{
|
|
if (update_global_scaling_factor (settings))
|
|
g_signal_emit (settings, signals[GLOBAL_SCALING_FACTOR_CHANGED], 0);
|
|
}
|
|
else if (g_str_equal (key, "text-scaling-factor"))
|
|
{
|
|
meta_settings_update_font_dpi (settings);
|
|
}
|
|
}
|
|
|
|
static void
|
|
privacy_settings_changed (GSettings *privacy_settings,
|
|
const char *key,
|
|
MetaSettings *settings)
|
|
{
|
|
if (g_str_equal (key, "privacy-screen"))
|
|
{
|
|
gboolean privacy_screen;
|
|
|
|
privacy_screen = g_settings_get_boolean (privacy_settings, key);
|
|
|
|
if (settings->privacy_screen != privacy_screen)
|
|
{
|
|
settings->privacy_screen = privacy_screen;
|
|
g_signal_emit (settings, signals[PRIVACY_SCREEN_CHANGED], 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
gboolean
|
|
meta_settings_is_experimental_feature_enabled (MetaSettings *settings,
|
|
MetaExperimentalFeature feature)
|
|
{
|
|
return !!(settings->experimental_features & feature);
|
|
}
|
|
|
|
void
|
|
meta_settings_override_experimental_features (MetaSettings *settings)
|
|
{
|
|
settings->experimental_features = META_EXPERIMENTAL_FEATURE_NONE;
|
|
settings->experimental_features_overridden = TRUE;
|
|
}
|
|
|
|
void
|
|
meta_settings_enable_experimental_feature (MetaSettings *settings,
|
|
MetaExperimentalFeature feature)
|
|
{
|
|
g_assert (settings->experimental_features_overridden);
|
|
|
|
settings->experimental_features |= feature;
|
|
}
|
|
|
|
static gboolean
|
|
experimental_features_handler (GVariant *features_variant,
|
|
gpointer *result,
|
|
gpointer data)
|
|
{
|
|
MetaSettings *settings = data;
|
|
GVariantIter features_iter;
|
|
char *feature_str;
|
|
MetaExperimentalFeature features = META_EXPERIMENTAL_FEATURE_NONE;
|
|
|
|
if (settings->experimental_features_overridden)
|
|
{
|
|
*result = GINT_TO_POINTER (FALSE);
|
|
return TRUE;
|
|
}
|
|
|
|
g_variant_iter_init (&features_iter, features_variant);
|
|
while (g_variant_iter_loop (&features_iter, "s", &feature_str))
|
|
{
|
|
MetaExperimentalFeature feature = META_EXPERIMENTAL_FEATURE_NONE;
|
|
|
|
if (g_str_equal (feature_str, "scale-monitor-framebuffer"))
|
|
feature = META_EXPERIMENTAL_FEATURE_SCALE_MONITOR_FRAMEBUFFER;
|
|
else if (g_str_equal (feature_str, "kms-modifiers"))
|
|
feature = META_EXPERIMENTAL_FEATURE_KMS_MODIFIERS;
|
|
else if (g_str_equal (feature_str, "rt-scheduler"))
|
|
feature = META_EXPERIMENTAL_FEATURE_RT_SCHEDULER;
|
|
else if (g_str_equal (feature_str, "autoclose-xwayland"))
|
|
feature = META_EXPERIMENTAL_FEATURE_AUTOCLOSE_XWAYLAND;
|
|
|
|
if (feature)
|
|
g_message ("Enabling experimental feature '%s'", feature_str);
|
|
else
|
|
g_warning ("Unknown experimental feature '%s'", feature_str);
|
|
|
|
features |= feature;
|
|
}
|
|
|
|
if (features != settings->experimental_features)
|
|
{
|
|
settings->experimental_features = features;
|
|
*result = GINT_TO_POINTER (TRUE);
|
|
}
|
|
else
|
|
{
|
|
*result = GINT_TO_POINTER (FALSE);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
update_experimental_features (MetaSettings *settings)
|
|
{
|
|
return GPOINTER_TO_INT (g_settings_get_mapped (settings->mutter_settings,
|
|
"experimental-features",
|
|
experimental_features_handler,
|
|
settings));
|
|
}
|
|
|
|
static void
|
|
mutter_settings_changed (GSettings *mutter_settings,
|
|
gchar *key,
|
|
MetaSettings *settings)
|
|
{
|
|
MetaExperimentalFeature old_experimental_features;
|
|
|
|
if (!g_str_equal (key, "experimental-features"))
|
|
return;
|
|
|
|
old_experimental_features = settings->experimental_features;
|
|
if (update_experimental_features (settings))
|
|
g_signal_emit (settings, signals[EXPERIMENTAL_FEATURES_CHANGED], 0,
|
|
(unsigned int) old_experimental_features);
|
|
}
|
|
|
|
static void
|
|
xwayland_grab_list_add_item (MetaSettings *settings,
|
|
char *item)
|
|
{
|
|
/* If first character is '!', it's a denied value */
|
|
if (item[0] != '!')
|
|
g_ptr_array_add (settings->xwayland_grab_allow_list_patterns,
|
|
g_pattern_spec_new (item));
|
|
else if (item[1] != 0)
|
|
g_ptr_array_add (settings->xwayland_grab_deny_list_patterns,
|
|
g_pattern_spec_new (&item[1]));
|
|
}
|
|
|
|
static gboolean
|
|
xwayland_grab_access_rules_handler (GVariant *variant,
|
|
gpointer *result,
|
|
gpointer data)
|
|
{
|
|
MetaSettings *settings = data;
|
|
GVariantIter iter;
|
|
char *item;
|
|
|
|
/* Create a GPatternSpec for each element */
|
|
g_variant_iter_init (&iter, variant);
|
|
while (g_variant_iter_loop (&iter, "s", &item))
|
|
xwayland_grab_list_add_item (settings, item);
|
|
|
|
*result = GINT_TO_POINTER (TRUE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
update_xwayland_grab_access_rules (MetaSettings *settings)
|
|
{
|
|
gchar **system_defaults;
|
|
int i;
|
|
|
|
/* Free previous patterns and create new arrays */
|
|
g_clear_pointer (&settings->xwayland_grab_allow_list_patterns,
|
|
g_ptr_array_unref);
|
|
settings->xwayland_grab_allow_list_patterns =
|
|
g_ptr_array_new_with_free_func ((GDestroyNotify) g_pattern_spec_free);
|
|
|
|
g_clear_pointer (&settings->xwayland_grab_deny_list_patterns,
|
|
g_ptr_array_unref);
|
|
settings->xwayland_grab_deny_list_patterns =
|
|
g_ptr_array_new_with_free_func ((GDestroyNotify) g_pattern_spec_free);
|
|
|
|
/* Add system defaults values */
|
|
system_defaults = g_strsplit (XWAYLAND_GRAB_DEFAULT_ACCESS_RULES, ",", -1);
|
|
for (i = 0; system_defaults[i]; i++)
|
|
xwayland_grab_list_add_item (settings, system_defaults[i]);
|
|
g_strfreev (system_defaults);
|
|
|
|
/* Then add gsettings values */
|
|
g_settings_get_mapped (settings->wayland_settings,
|
|
"xwayland-grab-access-rules",
|
|
xwayland_grab_access_rules_handler,
|
|
settings);
|
|
}
|
|
|
|
static void
|
|
update_xwayland_allow_grabs (MetaSettings *settings)
|
|
{
|
|
settings->xwayland_allow_grabs =
|
|
g_settings_get_boolean (settings->wayland_settings,
|
|
"xwayland-allow-grabs");
|
|
}
|
|
|
|
static void
|
|
update_xwayland_disable_extensions (MetaSettings *settings)
|
|
{
|
|
settings->xwayland_disable_extensions =
|
|
g_settings_get_flags (settings->wayland_settings,
|
|
"xwayland-disable-extension");
|
|
}
|
|
|
|
static void
|
|
update_privacy_settings (MetaSettings *settings)
|
|
{
|
|
privacy_settings_changed (settings->privacy_settings,
|
|
"privacy-screen",
|
|
settings);
|
|
}
|
|
|
|
static void
|
|
update_xwayland_allow_byte_swapped_clients (MetaSettings *settings)
|
|
{
|
|
|
|
settings->xwayland_allow_byte_swapped_clients =
|
|
g_settings_get_boolean (settings->wayland_settings,
|
|
"xwayland-allow-byte-swapped-clients");
|
|
}
|
|
|
|
static void
|
|
wayland_settings_changed (GSettings *wayland_settings,
|
|
gchar *key,
|
|
MetaSettings *settings)
|
|
{
|
|
|
|
if (g_str_equal (key, "xwayland-allow-grabs"))
|
|
{
|
|
update_xwayland_allow_grabs (settings);
|
|
}
|
|
else if (g_str_equal (key, "xwayland-grab-access-rules"))
|
|
{
|
|
update_xwayland_grab_access_rules (settings);
|
|
}
|
|
else if (g_str_equal (key, "xwayland-disable-extension"))
|
|
{
|
|
update_xwayland_disable_extensions (settings);
|
|
}
|
|
else if (g_str_equal (key, "xwayland-allow-byte-swapped-clients"))
|
|
{
|
|
update_xwayland_allow_byte_swapped_clients (settings);
|
|
}
|
|
}
|
|
|
|
void
|
|
meta_settings_get_xwayland_grab_patterns (MetaSettings *settings,
|
|
GPtrArray **allow_list_patterns,
|
|
GPtrArray **deny_list_patterns)
|
|
{
|
|
*allow_list_patterns = settings->xwayland_grab_allow_list_patterns;
|
|
*deny_list_patterns = settings->xwayland_grab_deny_list_patterns;
|
|
}
|
|
|
|
gboolean
|
|
meta_settings_are_xwayland_grabs_allowed (MetaSettings *settings)
|
|
{
|
|
return (settings->xwayland_allow_grabs);
|
|
}
|
|
|
|
int
|
|
meta_settings_get_xwayland_disable_extensions (MetaSettings *settings)
|
|
{
|
|
return (settings->xwayland_disable_extensions);
|
|
}
|
|
|
|
gboolean
|
|
meta_settings_are_xwayland_byte_swapped_clients_allowed (MetaSettings *settings)
|
|
{
|
|
return settings->xwayland_allow_byte_swapped_clients;
|
|
}
|
|
|
|
gboolean
|
|
meta_settings_is_privacy_screen_enabled (MetaSettings *settings)
|
|
{
|
|
return settings->privacy_screen;
|
|
}
|
|
|
|
void
|
|
meta_settings_set_privacy_screen_enabled (MetaSettings *settings,
|
|
gboolean enabled)
|
|
{
|
|
if (settings->privacy_screen == enabled)
|
|
return;
|
|
|
|
settings->privacy_screen = enabled;
|
|
g_settings_set_boolean (settings->privacy_settings, "privacy-screen",
|
|
enabled);
|
|
}
|
|
|
|
MetaSettings *
|
|
meta_settings_new (MetaBackend *backend)
|
|
{
|
|
MetaSettings *settings;
|
|
|
|
settings = g_object_new (META_TYPE_SETTINGS, NULL);
|
|
settings->backend = backend;
|
|
|
|
return settings;
|
|
}
|
|
|
|
static void
|
|
meta_settings_dispose (GObject *object)
|
|
{
|
|
MetaSettings *settings = META_SETTINGS (object);
|
|
|
|
g_clear_object (&settings->mutter_settings);
|
|
g_clear_object (&settings->interface_settings);
|
|
g_clear_object (&settings->privacy_settings);
|
|
g_clear_object (&settings->wayland_settings);
|
|
g_clear_pointer (&settings->xwayland_grab_allow_list_patterns,
|
|
g_ptr_array_unref);
|
|
g_clear_pointer (&settings->xwayland_grab_deny_list_patterns,
|
|
g_ptr_array_unref);
|
|
|
|
G_OBJECT_CLASS (meta_settings_parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
meta_settings_init (MetaSettings *settings)
|
|
{
|
|
settings->interface_settings = g_settings_new ("org.gnome.desktop.interface");
|
|
g_signal_connect (settings->interface_settings, "changed",
|
|
G_CALLBACK (interface_settings_changed),
|
|
settings);
|
|
settings->privacy_settings = g_settings_new ("org.gnome.desktop.privacy");
|
|
g_signal_connect (settings->privacy_settings, "changed",
|
|
G_CALLBACK (privacy_settings_changed),
|
|
settings);
|
|
settings->mutter_settings = g_settings_new ("org.gnome.mutter");
|
|
g_signal_connect (settings->mutter_settings, "changed",
|
|
G_CALLBACK (mutter_settings_changed),
|
|
settings);
|
|
settings->wayland_settings = g_settings_new ("org.gnome.mutter.wayland");
|
|
g_signal_connect (settings->wayland_settings, "changed",
|
|
G_CALLBACK (wayland_settings_changed),
|
|
settings);
|
|
|
|
/* Chain up inter-dependent settings. */
|
|
g_signal_connect (settings, "global-scaling-factor-changed",
|
|
G_CALLBACK (meta_settings_update_ui_scaling_factor), NULL);
|
|
g_signal_connect (settings, "ui-scaling-factor-changed",
|
|
G_CALLBACK (meta_settings_update_font_dpi), NULL);
|
|
|
|
update_global_scaling_factor (settings);
|
|
update_experimental_features (settings);
|
|
update_xwayland_grab_access_rules (settings);
|
|
update_xwayland_allow_grabs (settings);
|
|
update_xwayland_disable_extensions (settings);
|
|
update_privacy_settings (settings);
|
|
update_xwayland_allow_byte_swapped_clients (settings);
|
|
}
|
|
|
|
static void
|
|
on_monitors_changed (MetaMonitorManager *monitor_manager,
|
|
MetaSettings *settings)
|
|
{
|
|
meta_settings_update_ui_scaling_factor (settings);
|
|
}
|
|
|
|
void
|
|
meta_settings_post_init (MetaSettings *settings)
|
|
{
|
|
MetaMonitorManager *monitor_manager =
|
|
meta_backend_get_monitor_manager (settings->backend);
|
|
|
|
update_ui_scaling_factor (settings);
|
|
update_font_dpi (settings);
|
|
|
|
g_signal_connect_object (monitor_manager, "monitors-changed-internal",
|
|
G_CALLBACK (on_monitors_changed),
|
|
settings, G_CONNECT_AFTER);
|
|
}
|
|
|
|
static void
|
|
meta_settings_class_init (MetaSettingsClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
object_class->dispose = meta_settings_dispose;
|
|
|
|
signals[UI_SCALING_FACTOR_CHANGED] =
|
|
g_signal_new ("ui-scaling-factor-changed",
|
|
G_TYPE_FROM_CLASS (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
0,
|
|
NULL, NULL, NULL,
|
|
G_TYPE_NONE, 0);
|
|
|
|
signals[GLOBAL_SCALING_FACTOR_CHANGED] =
|
|
g_signal_new ("global-scaling-factor-changed",
|
|
G_TYPE_FROM_CLASS (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
0,
|
|
NULL, NULL, NULL,
|
|
G_TYPE_NONE, 0);
|
|
|
|
signals[FONT_DPI_CHANGED] =
|
|
g_signal_new ("font-dpi-changed",
|
|
G_TYPE_FROM_CLASS (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
0,
|
|
NULL, NULL, NULL,
|
|
G_TYPE_NONE, 0);
|
|
|
|
signals[EXPERIMENTAL_FEATURES_CHANGED] =
|
|
g_signal_new ("experimental-features-changed",
|
|
G_TYPE_FROM_CLASS (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
0,
|
|
NULL, NULL, NULL,
|
|
G_TYPE_NONE, 1, G_TYPE_UINT);
|
|
|
|
signals[PRIVACY_SCREEN_CHANGED] =
|
|
g_signal_new ("privacy-screen-changed",
|
|
G_TYPE_FROM_CLASS (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
0,
|
|
NULL, NULL, NULL,
|
|
G_TYPE_NONE, 0);
|
|
}
|