1
0
Fork 0

core: Add MetaSessionManager

This core object will be the manager of "client sessions", allowing
the windowing-specific paths to generate MetaSessionState objects to
track their clients.

This object is unused at the moment, and will be integrated in later
commits.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3825>
This commit is contained in:
Carlos Garnacho 2024-06-19 12:21:37 +02:00 committed by Marge Bot
parent c6acf3aef5
commit fa9c70a1d4
3 changed files with 473 additions and 0 deletions

View file

@ -0,0 +1,419 @@
/*
* Copyright (C) 2024 Red Hat Inc.
*
* 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/>.
*
* Author: Carlos Garnacho <carlosg@gnome.org>
*/
#include "config.h"
#include "core/meta-session-manager.h"
#include <fcntl.h>
#include <gio/gunixinputstream.h>
#include <glib/gstdio.h>
#include <gvdb/gvdb-builder.h>
#include <gvdb/gvdb-reader.h>
#include "core/meta-anonymous-file.h"
#define SESSION_FILE_NAME "session.gvdb"
struct _MetaSessionManager
{
GObject parent_instance;
GHashTable *sessions; /* Session name -> MetaSessionState */
GHashTable *deleted_sessions; /* Set of session names */
GvdbTable *gvdb_table;
char *name;
int fd;
GMappedFile *mapped_file;
};
enum
{
PROP_0,
PROP_NAME,
PROP_FD,
N_PROPS,
};
static GParamSpec *props[N_PROPS] = { 0, };
#define MAX_SIZE (10 * 1024 * 1024)
static void meta_session_manager_initable_iface_init (GInitableIface *iface);
G_DEFINE_FINAL_TYPE_WITH_CODE (MetaSessionManager, meta_session_manager,
G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
meta_session_manager_initable_iface_init))
static void
meta_session_manager_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
MetaSessionManager *session_manager = META_SESSION_MANAGER (object);
switch (prop_id)
{
case PROP_NAME:
session_manager->name = g_value_dup_string (value);
break;
case PROP_FD:
session_manager->fd = g_value_get_int (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
meta_session_manager_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
MetaSessionManager *session_manager = META_SESSION_MANAGER (object);
switch (prop_id)
{
case PROP_NAME:
g_value_set_string (value, session_manager->name);
break;
case PROP_FD:
g_value_set_int (value, session_manager->fd);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
meta_session_manager_finalize (GObject *object)
{
MetaSessionManager *session_manager = META_SESSION_MANAGER (object);
g_clear_pointer (&session_manager->sessions, g_hash_table_unref);
g_clear_pointer (&session_manager->deleted_sessions, g_hash_table_unref);
g_clear_pointer (&session_manager->mapped_file, g_mapped_file_unref);
g_clear_pointer (&session_manager->gvdb_table, gvdb_table_free);
g_clear_pointer (&session_manager->name, g_free);
g_clear_fd (&session_manager->fd, NULL);
G_OBJECT_CLASS (meta_session_manager_parent_class)->finalize (object);
}
static void
meta_session_manager_class_init (MetaSessionManagerClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->set_property = meta_session_manager_set_property;
object_class->get_property = meta_session_manager_get_property;
object_class->finalize = meta_session_manager_finalize;
props[PROP_NAME] =
g_param_spec_string ("name", NULL, NULL,
NULL,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY);
props[PROP_FD] =
g_param_spec_int ("fd", NULL, NULL,
G_MININT, G_MAXINT, -1,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY);
g_object_class_install_properties (object_class, N_PROPS, props);
}
static void
meta_session_manager_init (MetaSessionManager *session_manager)
{
session_manager->fd = -1;
session_manager->sessions =
g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref);
session_manager->deleted_sessions =
g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
}
static gboolean
meta_session_manager_initable_init (GInitable *initable,
GCancellable *cancellable,
GError **error)
{
MetaSessionManager *manager = META_SESSION_MANAGER (initable);
g_autoptr (GBytes) bytes = NULL;
if (manager->name && manager->fd < 0)
{
g_autofree char *session_dir = NULL, *session_file = NULL;
session_dir = g_build_filename (g_get_user_data_dir (),
manager->name, NULL);
if (g_mkdir_with_parents (session_dir, 0700) < 0)
{
g_set_error (error,
G_IO_ERROR,
g_io_error_from_errno (errno),
"Could not create directory for session data: %m");
return FALSE;
}
session_file = g_build_filename (session_dir,
SESSION_FILE_NAME,
NULL);
manager->fd = open (session_file, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
}
if (manager->fd < 0)
{
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
"Error opening session file: %m");
return FALSE;
}
manager->mapped_file = g_mapped_file_new_from_fd (manager->fd, TRUE, error);
if (!manager->mapped_file)
return FALSE;
if (g_mapped_file_get_length (manager->mapped_file) > 0)
{
bytes = g_mapped_file_get_bytes (manager->mapped_file);
manager->gvdb_table = gvdb_table_new_from_bytes (bytes, FALSE, error);
if (!manager->gvdb_table)
return FALSE;
}
return TRUE;
}
static void
meta_session_manager_initable_iface_init (GInitableIface *iface)
{
iface->init = meta_session_manager_initable_init;
}
MetaSessionManager *
meta_session_manager_new (const gchar *name,
GError **error)
{
return g_initable_new (META_TYPE_SESSION_MANAGER, NULL, error,
"name", name,
NULL);
}
MetaSessionManager *
meta_session_manager_new_for_fd (const gchar *name,
int fd,
GError **error)
{
return g_initable_new (META_TYPE_SESSION_MANAGER,
NULL, error,
"name", name,
"fd", fd,
NULL);
}
int
meta_session_manager_get_fd (MetaSessionManager *manager)
{
return manager->fd;
}
gboolean
meta_session_manager_get_session_exists (MetaSessionManager *manager,
const char *name)
{
if (g_hash_table_contains (manager->sessions, name))
return TRUE;
if (g_hash_table_contains (manager->deleted_sessions, name))
return FALSE;
if (manager->gvdb_table)
{
GvdbTable *table;
table = gvdb_table_get_table (manager->gvdb_table, name);
if (table)
{
gvdb_table_free (table);
return TRUE;
}
}
return FALSE;
}
MetaSessionState *
meta_session_manager_get_session (MetaSessionManager *manager,
GType type,
const gchar *name)
{
g_autoptr (MetaSessionState) session_state = NULL;
GvdbTable *table = NULL;
g_assert (g_type_is_a (type, META_TYPE_SESSION_STATE));
session_state = g_hash_table_lookup (manager->sessions, name);
if (session_state)
return g_steal_pointer (&session_state);
session_state = g_object_new (type, "name", name, NULL);
if (manager->gvdb_table)
table = gvdb_table_get_table (manager->gvdb_table, name);
if (table)
{
g_autoptr (GError) error = NULL;
meta_session_state_parse (session_state, table, &error);
g_clear_pointer (&table, gvdb_table_free);
if (error)
{
g_critical ("Error parsing session data: %s\n", error->message);
/* Ensure to return a pristine state */
g_clear_object (&session_state);
session_state = g_object_new (type, "name", name, NULL);
}
}
g_hash_table_insert (manager->sessions,
(gpointer) meta_session_state_get_name (session_state),
g_object_ref (session_state));
return g_steal_pointer (&session_state);
}
void
meta_session_manager_delete_session (MetaSessionManager *manager,
const char *name)
{
g_hash_table_add (manager->deleted_sessions, g_strdup (name));
g_hash_table_remove (manager->sessions, name);
}
static void
snapshot_gvdb_recursively (GvdbTable *table,
GHashTable *dest,
const gchar *name)
{
g_autoptr (GVariant) value = NULL;
value = gvdb_table_get_value (table, name);
if (value)
{
GvdbItem *item;
item = gvdb_hash_table_insert (dest, name);
gvdb_item_set_value (item, value);
}
else
{
GvdbTable *subtable;
GHashTable *dest_subtable;
g_auto (GStrv) names;
size_t len, i;
subtable = gvdb_table_get_table (table, name);
names = gvdb_table_get_names (subtable, &len);
dest_subtable = gvdb_hash_table_new (dest, name);
for (i = 0; i < len; i++)
snapshot_gvdb_recursively (subtable, dest_subtable, names[i]);
gvdb_table_free (subtable);
}
}
gboolean
meta_session_manager_save_sync (MetaSessionManager *manager,
GError **error)
{
GHashTableIter iter;
GHashTable *table;
MetaSessionState *session_state;
g_autofree char *session_dir = NULL, *session_file = NULL;
if (!manager->name)
{
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
"Context does not have a name");
return FALSE;
}
session_dir = g_build_filename (g_get_user_data_dir (),
manager->name,
NULL);
if (g_mkdir_with_parents (session_dir, 0700) < 0)
{
g_set_error (error,
G_IO_ERROR,
g_io_error_from_errno (errno),
"Could not create directory for session data: %m");
return FALSE;
}
session_file = g_build_filename (session_dir, SESSION_FILE_NAME, NULL);
g_hash_table_iter_init (&iter, manager->sessions);
table = gvdb_hash_table_new (NULL, NULL);
while (g_hash_table_iter_next (&iter, NULL, (gpointer*) &session_state))
{
GHashTable *session_table;
session_table =
gvdb_hash_table_new (table, meta_session_state_get_name (session_state));
meta_session_state_serialize (session_state, session_table);
}
if (manager->gvdb_table)
{
g_auto (GStrv) names;
gsize len, i;
names = gvdb_table_get_names (manager->gvdb_table, &len);
for (i = 0; i < len; i++)
{
if (g_hash_table_contains (manager->sessions, names[i]))
continue;
if (g_hash_table_contains (manager->deleted_sessions, names[i]))
continue;
snapshot_gvdb_recursively (manager->gvdb_table, table, names[i]);
}
}
return gvdb_table_write_contents (table,
session_file,
FALSE, error);
}

View file

@ -0,0 +1,53 @@
/*
* Copyright (C) 2024 Red Hat Inc.
*
* 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/>.
*
* Author: Carlos Garnacho <carlosg@gnome.org>
*/
#pragma once
#include <sys/mman.h>
#include "core/meta-session-state.h"
#include "meta/window.h"
#define META_TYPE_SESSION_MANAGER (meta_session_manager_get_type ())
G_DECLARE_FINAL_TYPE (MetaSessionManager,
meta_session_manager,
META, SESSION_MANAGER,
GObject)
MetaSessionManager * meta_session_manager_new (const gchar *name,
GError **error);
MetaSessionManager * meta_session_manager_new_for_fd (const gchar *name,
int fd,
GError **error);
int meta_session_manager_get_fd (MetaSessionManager *manager);
gboolean meta_session_manager_get_session_exists (MetaSessionManager *manager,
const char *name);
MetaSessionState * meta_session_manager_get_session (MetaSessionManager *manager,
GType type,
const char *name);
gboolean meta_session_manager_save_sync (MetaSessionManager *manager,
GError **error);
void meta_session_manager_delete_session (MetaSessionManager *manager,
const char *name);

View file

@ -372,6 +372,7 @@ mutter_sources = [
'core/meta-selection.c', 'core/meta-selection.c',
'core/meta-selection-source.c', 'core/meta-selection-source.c',
'core/meta-selection-source-memory.c', 'core/meta-selection-source-memory.c',
'core/meta-session-manager.c',
'core/meta-session-state.c', 'core/meta-session-state.c',
'core/meta-sound-player.c', 'core/meta-sound-player.c',
'core/meta-tablet-action-mapper.c', 'core/meta-tablet-action-mapper.c',