1
0
Fork 0

wayland: Add MetaWaylandXdgSessionState

This object is a windowing-specific implementation of MetaSessionState,
allowing to save window state for toplevel surfaces of a Wayland client
using the xdg_session_management_v1 protocol.

This object is detached from windowing logic itself, and will be
integrated in later commits.

Changes from Carlos Garnacho: Integrate state serialization with
MetaSessionState and MetaSessionManager.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3825>
This commit is contained in:
Jonas Ådahl 2024-06-19 12:38:37 +02:00 committed by Marge Bot
parent 920de400f5
commit 6fa3a91325
4 changed files with 522 additions and 0 deletions

View file

@ -709,6 +709,8 @@ if have_wayland
'wayland/meta-wayland-xdg-foreign.c',
'wayland/meta-wayland-xdg-foreign.h',
'wayland/meta-wayland-xdg-foreign-private.h',
'wayland/meta-wayland-xdg-session-state.c',
'wayland/meta-wayland-xdg-session-state.h',
'wayland/meta-wayland-xdg-shell.c',
'wayland/meta-wayland-xdg-shell.h',
'wayland/meta-wayland-xdg-dialog.c',

View file

@ -73,3 +73,5 @@ typedef struct _MetaWaylandFilterManager MetaWaylandFilterManager;
typedef struct _MetaWaylandClient MetaWaylandClient;
typedef struct _MetaWaylandDrmLeaseManager MetaWaylandDrmLeaseManager;
typedef struct _MetaWaylandXdgSessionManager MetaWaylandXdgSessionManager;

View file

@ -0,0 +1,486 @@
/*
* Copyright (C) 2024 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 "wayland/meta-wayland-xdg-session-state.h"
#include <gio/gio.h>
#include "backends/meta-monitor.h"
#include "core/meta-context-private.h"
#include "core/window-private.h"
#include "wayland/meta-wayland.h"
#define STATE_FORMAT_VERSION 1
typedef enum _WindowState
{
WINDOW_STATE_NONE = 0,
/* floating */
WINDOW_STATE_FLOATING = 1,
/* tiling */
WINDOW_STATE_MAXIMIZED = 2,
WINDOW_STATE_TILED_LEFT = 3,
WINDOW_STATE_TILED_RIGHT = 4,
} WindowState;
struct _MetaWaylandXdgToplevelState
{
MetaWaylandXdgSessionState *session_state;
WindowState window_state;
struct {
MtkRectangle rect;
} floating;
struct {
MtkRectangle rect;
} tiled;
gboolean is_minimized;
int workspace_idx;
};
struct _MetaWaylandXdgSessionState
{
MetaSessionState parent;
GHashTable *toplevels;
};
G_DEFINE_TYPE (MetaWaylandXdgSessionState,
meta_wayland_xdg_session_state,
META_TYPE_SESSION_STATE)
static GVariant *
new_rect_variant (const MtkRectangle *rect)
{
return g_variant_new ("(iiii)",
rect->x,
rect->y,
rect->width,
rect->height);
}
static void
variant_to_rect (GVariant *variant,
MtkRectangle *rect)
{
g_variant_get (variant, "(iiii)",
&rect->x,
&rect->y,
&rect->width,
&rect->height);
}
static void
meta_wayland_xdg_toplevel_state_free (MetaWaylandXdgToplevelState *toplevel_state)
{
g_free (toplevel_state);
}
static const char *
window_state_to_string (WindowState state)
{
switch (state)
{
case WINDOW_STATE_NONE:
return "none";
case WINDOW_STATE_FLOATING:
return "floating";
case WINDOW_STATE_MAXIMIZED:
return "maximized";
case WINDOW_STATE_TILED_LEFT:
return "tiled-left";
case WINDOW_STATE_TILED_RIGHT:
return "tiled-right";
}
g_assert_not_reached ();
}
static char *
meta_wayland_xdg_toplevel_state_to_string (MetaWaylandXdgToplevelState *state)
{
GString *str = NULL;
str = g_string_new (NULL);
g_string_append (str, window_state_to_string (state->window_state));
switch (state->window_state)
{
case WINDOW_STATE_NONE:
break;
case WINDOW_STATE_FLOATING:
g_string_append_printf (str, " Rect [%d,%d +%d,%d]",
state->floating.rect.x,
state->floating.rect.y,
state->floating.rect.width,
state->floating.rect.height);
break;
case WINDOW_STATE_MAXIMIZED:
case WINDOW_STATE_TILED_LEFT:
case WINDOW_STATE_TILED_RIGHT:
g_string_append_printf (str, " Rect [%d,%d +%d,%d]",
state->tiled.rect.x,
state->tiled.rect.y,
state->tiled.rect.width,
state->tiled.rect.height);
break;
}
return g_string_free_and_steal (str);
}
static MetaWaylandXdgToplevelState *
meta_wayland_xdg_session_state_ensure_toplevel (MetaWaylandXdgSessionState *session_state,
const char *name)
{
MetaWaylandXdgToplevelState *toplevel_state;
toplevel_state = g_hash_table_lookup (session_state->toplevels, name);
if (!toplevel_state)
{
toplevel_state = g_new0 (MetaWaylandXdgToplevelState, 1);
toplevel_state->session_state = session_state;
g_hash_table_insert (session_state->toplevels,
g_strdup (name), toplevel_state);
}
return toplevel_state;
}
static void
meta_wayland_xdg_session_state_dispose (GObject *object)
{
MetaWaylandXdgSessionState *session_state =
META_WAYLAND_XDG_SESSION_STATE (object);
g_clear_pointer (&session_state->toplevels, g_hash_table_unref);
G_OBJECT_CLASS (meta_wayland_xdg_session_state_parent_class)->dispose (object);
}
static gboolean
meta_wayland_xdg_session_state_serialize (MetaSessionState *session_state,
GHashTable *gvdb_data)
{
MetaWaylandXdgSessionState *xdg_session_state =
META_WAYLAND_XDG_SESSION_STATE (session_state);
GHashTable *toplevels;
GHashTableIter iter;
gpointer key, value;
GvdbItem *item;
item = gvdb_hash_table_insert (gvdb_data, "version");
gvdb_item_set_value (item, g_variant_new ("i", STATE_FORMAT_VERSION));
item = gvdb_hash_table_insert (gvdb_data, "last-used");
gvdb_item_set_value (item, g_variant_new ("x", g_get_real_time ()));
toplevels = gvdb_hash_table_new (gvdb_data, "toplevels");
g_hash_table_iter_init (&iter, xdg_session_state->toplevels);
while (g_hash_table_iter_next (&iter, &key, &value))
{
char *name = key;
MetaWaylandXdgToplevelState *toplevel_state = value;
GHashTable *toplevel;
meta_topic (META_DEBUG_SESSION_MANAGEMENT,
"Serializing toplevel state %s", name);
toplevel = gvdb_hash_table_new (toplevels, name);
item = gvdb_hash_table_insert (toplevel, "state");
gvdb_item_set_value (item, g_variant_new ("u", toplevel_state->window_state));
switch (toplevel_state->window_state)
{
case WINDOW_STATE_NONE:
break;
case WINDOW_STATE_FLOATING:
item = gvdb_hash_table_insert (toplevel, "floating-rect");
gvdb_item_set_value (item, new_rect_variant (&toplevel_state->floating.rect));
break;
case WINDOW_STATE_MAXIMIZED:
case WINDOW_STATE_TILED_LEFT:
case WINDOW_STATE_TILED_RIGHT:
item = gvdb_hash_table_insert (toplevel, "tiled-rect");
gvdb_item_set_value (item, new_rect_variant (&toplevel_state->tiled.rect));
break;
}
item = gvdb_hash_table_insert (toplevel, "is-minimized");
gvdb_item_set_value (item, g_variant_new_boolean (toplevel_state->is_minimized));
item = gvdb_hash_table_insert (toplevel, "workspace");
gvdb_item_set_value (item,
g_variant_new_int32 (toplevel_state->workspace_idx));
}
return TRUE;
}
static gboolean
meta_wayland_xdg_session_state_parse (MetaSessionState *session_state,
GvdbTable *data,
GError **error)
{
MetaWaylandXdgSessionState *xdg_session_state =
META_WAYLAND_XDG_SESSION_STATE (session_state);
g_autoptr (GVariant) version = NULL;
GvdbTable *toplevels, *toplevel;
GStrv toplevel_names = NULL;
int i;
version = gvdb_table_get_value (data, "version");
if (!g_variant_is_of_type (version, G_VARIANT_TYPE ("i")) ||
g_variant_get_int32 (version) > STATE_FORMAT_VERSION)
{
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
"Too new session-data version");
return FALSE;
}
toplevels = gvdb_table_get_table (data, "toplevels");
toplevel_names = gvdb_table_get_names (toplevels, NULL);
for (i = 0; toplevel_names[i]; i++)
{
MetaWaylandXdgToplevelState * toplevel_state;
g_autoptr (GVariant) state = NULL, floating_rect = NULL, tiled_rect = NULL;
g_autoptr (GVariant) is_minimized = NULL, monitor = NULL, workspace = NULL;
meta_topic (META_DEBUG_SESSION_MANAGEMENT,
"Parsing toplevel state %s", toplevel_names[i]);
toplevel_state =
meta_wayland_xdg_session_state_ensure_toplevel (xdg_session_state,
toplevel_names[i]);
toplevel = gvdb_table_get_table (toplevels, toplevel_names[i]);
state = gvdb_table_get_value (toplevel, "state");
if (state && g_variant_is_of_type (state, G_VARIANT_TYPE ("u")))
toplevel_state->window_state = g_variant_get_uint32 (state);
floating_rect = gvdb_table_get_value (toplevel, "floating-rect");
if (floating_rect && g_variant_is_of_type (floating_rect, G_VARIANT_TYPE ("(iiii)")))
{
variant_to_rect (floating_rect, &toplevel_state->floating.rect);
}
tiled_rect = gvdb_table_get_value (toplevel, "tiled-rect");
if (tiled_rect && g_variant_is_of_type (tiled_rect, G_VARIANT_TYPE ("(iiii)")))
{
variant_to_rect (floating_rect, &toplevel_state->tiled.rect);
}
is_minimized = gvdb_table_get_value (toplevel, "is-minimized");
if (state && g_variant_is_of_type (state, G_VARIANT_TYPE ("b")))
toplevel_state->is_minimized = g_variant_get_boolean (is_minimized);
workspace = gvdb_table_get_value (toplevel, "workspace");
if (workspace && g_variant_is_of_type (workspace, G_VARIANT_TYPE ("i")))
toplevel_state->workspace_idx = g_variant_get_int32 (workspace);
gvdb_table_free (toplevel);
}
g_clear_pointer (&toplevel_names, g_strfreev);
gvdb_table_free (toplevels);
return TRUE;
}
static void
meta_wayland_xdg_session_state_save_window (MetaSessionState *state,
const char *name,
MetaWindow *window)
{
MetaWaylandXdgSessionState *xdg_session_state =
META_WAYLAND_XDG_SESSION_STATE (state);
MetaWaylandXdgToplevelState *toplevel_state;
toplevel_state =
meta_wayland_xdg_session_state_ensure_toplevel (xdg_session_state,
name);
g_object_get (window,
"minimized", &toplevel_state->is_minimized,
NULL);
if (meta_window_get_maximized (window) ==
(META_MAXIMIZE_VERTICAL | META_MAXIMIZE_HORIZONTAL))
{
toplevel_state->window_state = WINDOW_STATE_MAXIMIZED;
toplevel_state->tiled.rect = window->rect;
}
else if (window->tile_mode == META_TILE_LEFT ||
window->tile_mode == META_TILE_RIGHT)
{
if (window->tile_mode == META_TILE_LEFT)
toplevel_state->window_state = WINDOW_STATE_TILED_LEFT;
else if (window->tile_mode == META_TILE_RIGHT)
toplevel_state->window_state = WINDOW_STATE_TILED_RIGHT;
toplevel_state->tiled.rect = window->rect;
}
else
{
toplevel_state->window_state = WINDOW_STATE_FLOATING;
toplevel_state->floating.rect = window->rect;
}
toplevel_state->workspace_idx = meta_workspace_index (window->workspace);
if (meta_is_topic_enabled (META_DEBUG_SESSION_MANAGEMENT))
{
g_autofree char *state_str = NULL;
state_str =
meta_wayland_xdg_toplevel_state_to_string (toplevel_state);
meta_topic (META_DEBUG_SESSION_MANAGEMENT,
"Saved window state %s: %s", name, state_str);
}
}
static gboolean
meta_wayland_xdg_session_state_restore_window (MetaSessionState *state,
const char *name,
MetaWindow *window)
{
MetaWaylandXdgSessionState *xdg_session_state =
META_WAYLAND_XDG_SESSION_STATE (state);
MetaWaylandXdgToplevelState *toplevel_state;
MtkRectangle *rect = NULL;
toplevel_state = g_hash_table_lookup (xdg_session_state->toplevels, name);
if (!toplevel_state)
return FALSE;
switch (toplevel_state->window_state)
{
case WINDOW_STATE_NONE:
break;
case WINDOW_STATE_FLOATING:
rect = &toplevel_state->floating.rect;
break;
case WINDOW_STATE_TILED_LEFT:
case WINDOW_STATE_TILED_RIGHT:
case WINDOW_STATE_MAXIMIZED:
break;
}
switch (toplevel_state->window_state)
{
case WINDOW_STATE_NONE:
case WINDOW_STATE_FLOATING:
break;
case WINDOW_STATE_TILED_LEFT:
meta_window_tile (window, META_TILE_LEFT);
break;
case WINDOW_STATE_TILED_RIGHT:
meta_window_tile (window, META_TILE_RIGHT);
break;
case WINDOW_STATE_MAXIMIZED:
meta_window_maximize (window, META_MAXIMIZE_VERTICAL |
META_MAXIMIZE_HORIZONTAL);
break;
}
if (toplevel_state->is_minimized)
meta_window_minimize (window);
if (toplevel_state->workspace_idx >= 0)
{
meta_window_change_workspace_by_index (window,
toplevel_state->workspace_idx,
TRUE);
}
if (rect)
{
meta_window_move_resize_internal (window,
(META_MOVE_RESIZE_WAYLAND_CLIENT_RESIZE |
META_MOVE_RESIZE_WAYLAND_FINISH_MOVE_RESIZE |
META_MOVE_RESIZE_MOVE_ACTION |
META_MOVE_RESIZE_RESIZE_ACTION),
META_PLACE_FLAG_NONE,
META_GRAVITY_NORTH_WEST,
*rect);
}
window->placed = TRUE;
if (meta_is_topic_enabled (META_DEBUG_SESSION_MANAGEMENT))
{
g_autofree char *state_str = NULL;
state_str =
meta_wayland_xdg_toplevel_state_to_string (toplevel_state);
meta_topic (META_DEBUG_SESSION_MANAGEMENT,
"Restored window state %s: %s", name, state_str);
}
return TRUE;
}
static void
meta_wayland_xdg_session_state_remove_window (MetaSessionState *state,
const char *name)
{
MetaWaylandXdgSessionState *xdg_session_state =
META_WAYLAND_XDG_SESSION_STATE (state);
g_hash_table_remove (xdg_session_state->toplevels, name);
}
static void
meta_wayland_xdg_session_state_class_init (MetaWaylandXdgSessionStateClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
MetaSessionStateClass *session_state_class = META_SESSION_STATE_CLASS (klass);
object_class->dispose = meta_wayland_xdg_session_state_dispose;
session_state_class->serialize = meta_wayland_xdg_session_state_serialize;
session_state_class->parse = meta_wayland_xdg_session_state_parse;
session_state_class->save_window = meta_wayland_xdg_session_state_save_window;
session_state_class->restore_window =
meta_wayland_xdg_session_state_restore_window;
session_state_class->remove_window =
meta_wayland_xdg_session_state_remove_window;
}
static void
meta_wayland_xdg_session_state_init (MetaWaylandXdgSessionState *session_state)
{
session_state->toplevels =
g_hash_table_new_full (g_str_hash, g_str_equal,
g_free,
(GDestroyNotify) meta_wayland_xdg_toplevel_state_free);
}

View file

@ -0,0 +1,32 @@
/*
* Copyright (C) 2024 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/>.
*
*/
#pragma once
#include <glib-object.h>
#include "core/meta-session-state.h"
#include "wayland/meta-wayland-types.h"
typedef struct _MetaWaylandXdgToplevelState MetaWaylandXdgToplevelState;
#define META_TYPE_WAYLAND_XDG_SESSION_STATE (meta_wayland_xdg_session_state_get_type ())
G_DECLARE_FINAL_TYPE (MetaWaylandXdgSessionState,
meta_wayland_xdg_session_state,
META, WAYLAND_XDG_SESSION_STATE,
MetaSessionState)