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:
parent
c6acf3aef5
commit
fa9c70a1d4
3 changed files with 473 additions and 0 deletions
419
src/core/meta-session-manager.c
Normal file
419
src/core/meta-session-manager.c
Normal 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);
|
||||
}
|
53
src/core/meta-session-manager.h
Normal file
53
src/core/meta-session-manager.h
Normal 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);
|
|
@ -372,6 +372,7 @@ mutter_sources = [
|
|||
'core/meta-selection.c',
|
||||
'core/meta-selection-source.c',
|
||||
'core/meta-selection-source-memory.c',
|
||||
'core/meta-session-manager.c',
|
||||
'core/meta-session-state.c',
|
||||
'core/meta-sound-player.c',
|
||||
'core/meta-tablet-action-mapper.c',
|
||||
|
|
Loading…
Add table
Reference in a new issue